From 129c39448c8f44342861957def7e27838507b4a8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 8 May 2026 11:58:12 +0200 Subject: [PATCH] Reminders (#5669) * Extract notification command for using the correct spacing * Add easy to set reminders * More human * Clear and direct hotkeys * Split * Bake reminders into the skill --- bin/omarchy | 1 + bin/omarchy-menu | 46 ++++++++- bin/omarchy-reminder | 139 +++++++++++++++++++++++++++ default/hypr/bindings/utilities.conf | 7 +- default/omarchy-skill/SKILL.md | 21 +++- 5 files changed, 209 insertions(+), 5 deletions(-) create mode 100755 bin/omarchy-reminder diff --git a/bin/omarchy b/bin/omarchy index d4fd2570..eda79a74 100755 --- a/bin/omarchy +++ b/bin/omarchy @@ -54,6 +54,7 @@ GROUP_DESCRIPTIONS[plymouth]="Plymouth boot theme management" GROUP_DESCRIPTIONS[powerprofiles]="Power profile management" GROUP_DESCRIPTIONS[refresh]="Reset config to defaults" GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows" +GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders" GROUP_DESCRIPTIONS[remove]="Removal workflows" GROUP_DESCRIPTIONS[restart]="Restart Omarchy components" GROUP_DESCRIPTIONS[setup]="Interactive setup wizards" diff --git a/bin/omarchy-menu b/bin/omarchy-menu index ec9a5aae..c8746cfb 100755 --- a/bin/omarchy-menu +++ b/bin/omarchy-menu @@ -43,6 +43,12 @@ menu() { echo -e "$options" | omarchy-launch-walker --dmenu --width 295 --minheight 1 --maxheight 630 -p "$prompt…" "${args[@]}" 2>/dev/null } +input() { + local prompt="$1" + + omarchy-launch-walker --dmenu --inputonly --width 295 --minheight 1 --maxheight 1 -p "$prompt…" 2>/dev/null +} + terminal() { xdg-terminal-exec --app-id=org.omarchy.terminal "$@" } @@ -93,7 +99,8 @@ show_learn_menu() { } show_trigger_menu() { - case $(menu "Trigger" " Capture\n󰧸 Transcode\n Share\n󰔎 Toggle\n Hardware") in + case $(menu "Trigger" "󰔛 Reminder\n Capture\n󰧸 Transcode\n Share\n󰔎 Toggle\n Hardware") in + *Reminder*) show_reminder_menu ;; *Capture*) show_capture_menu ;; *Transcode*) show_transcode_menu ;; *Share*) show_share_menu ;; @@ -103,6 +110,41 @@ show_trigger_menu() { esac } +show_reminder_menu() { + case $(menu "Reminder" "󰔛 Set one\n󰔛 Show all\n󰔛 Clear all") in + *Set*) show_custom_reminder_input ;; + *"Show all"*) omarchy-reminder show ;; + *"Clear all"*) omarchy-reminder clear ;; + *) back_to show_trigger_menu ;; + esac +} + +show_custom_reminder_input() { + local minutes + minutes=$(input "Remind in minutes") + + if [[ $minutes =~ ^[0-9]+$ ]] && ((minutes > 0)); then + show_reminder_message_input "$minutes" + elif [[ -n $minutes ]]; then + omarchy-notification-send "󰔛" "Invalid reminder" "Enter the number of minutes" -u critical + show_custom_reminder_input + else + back_to show_reminder_menu + fi +} + +show_reminder_message_input() { + local minutes="$1" + local message + message=$(input "Reminder message") + + if [[ -n $message ]]; then + omarchy-reminder "$minutes" "$message" + else + omarchy-reminder "$minutes" + fi +} + show_capture_menu() { case $(menu "Capture" " Screenshot\n Screenrecord\n󰴑 Text Extraction\n󰃉 Color") in *Screenshot*) omarchy-capture-screenshot ;; @@ -816,6 +858,8 @@ go_to_menu() { *hardware*) show_hardware_menu ;; *share*) show_share_menu ;; *transcode*) show_transcode_menu ;; + *reminder-set*) show_custom_reminder_input ;; + *reminder*) show_reminder_menu ;; *background*) show_background_menu ;; *capture*) show_capture_menu ;; *style*) show_style_menu ;; diff --git a/bin/omarchy-reminder b/bin/omarchy-reminder new file mode 100755 index 00000000..1dc59097 --- /dev/null +++ b/bin/omarchy-reminder @@ -0,0 +1,139 @@ +#!/bin/bash + +# omarchy:summary=Set and show lightweight desktop notification reminders +# omarchy:args= [message] | show | clear +# omarchy:examples=omarchy reminder 5 | omarchy reminder 30 "Check the oven" | omarchy reminder show | omarchy reminder clear + +set -euo pipefail + +format_remaining() { + local seconds=$1 + local minutes=$((seconds / 60)) + local remainder=$((seconds % 60)) + + if ((minutes > 0 && remainder > 0)); then + echo "${minutes}m ${remainder}s" + elif ((minutes > 0)); then + echo "${minutes}m" + else + echo "${remainder}s" + fi +} + +parse_systemd_timespan() { + local timespan="$1" + local total=0 + local value unit whole + + while read -r value unit; do + whole=${value%.*} + + case $unit in + d) total=$((total + whole * 86400)) ;; + h) total=$((total + whole * 3600)) ;; + min) total=$((total + whole * 60)) ;; + s) total=$((total + whole)) ;; + ms | us) ;; + esac + done < <(grep -oE '[0-9]+([.][0-9]+)?(d|h|min|s|ms|us)' <<<"$timespan" | sed -E 's/^([0-9.]+)([a-z]+)$/\1 \2/') + + echo "$total" +} + +show_reminders() { + local timers timer next next_seconds uptime remaining body="" + local reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" + local reminder_message="" + + timers=$(systemctl --user list-timers --all --no-legend --no-pager "omarchy-reminder-*.timer" 2>/dev/null | awk '{ print $(NF - 1) }') + uptime=${SECONDS_SINCE_BOOT:-$(awk '{ print int($1) }' /proc/uptime)} + + for timer in $timers; do + next=$(systemctl --user show -P NextElapseUSecMonotonic "$timer" 2>/dev/null || true) + [[ -z $next ]] && continue + + next_seconds=$(parse_systemd_timespan "$next") + ((next_seconds <= uptime)) && continue + + remaining=$((next_seconds - uptime)) + reminder=${timer%.timer} + reminder=${reminder#omarchy-reminder-} + set_at=${reminder##*-} + reminder_minutes=${reminder%%m-*} + reminder_message="" + [[ -f $reminder_dir/${timer%.timer}.message ]] && reminder_message=$(<"$reminder_dir/${timer%.timer}.message") + + if [[ -n $reminder_message ]]; then + body+="$reminder_message in $(format_remaining $remaining) ($(date -d "@$((set_at + reminder_minutes * 60))" +%-H:%M))"$'\n' + else + body+="${reminder_minutes}-min reminder in $(format_remaining $remaining) ($(date -d "@$((set_at + reminder_minutes * 60))" +%-H:%M))"$'\n' + fi + done + + if [[ -z $body ]]; then + omarchy-notification-send "󰔛" "Upcoming reminders" "No outstanding reminders" -u low + else + omarchy-notification-send "󰔛" "Upcoming reminders" "${body%$'\n'}" -u low + fi +} + +clear_reminders() { + local units + local reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" + + units=$(systemctl --user list-timers --all --no-legend --no-pager "omarchy-reminder-*.timer" 2>/dev/null | awk '{ print $(NF - 1), $NF }') + + if [[ -n $units ]]; then + xargs -r systemctl --user stop <<<"$units" || true + fi + + rm -f "$reminder_dir"/omarchy-reminder-*.message 2>/dev/null || true + omarchy-notification-send "󰔛" "All reminders have been cleared" -u low +} + +case ${1:-} in +show | list) + show_reminders + exit 0 + ;; +clear) + clear_reminders + exit 0 + ;; +esac + +minutes=${1:-} +shift || true +message="$*" +custom_message="$message" + +if [[ -z $minutes ]] || [[ ! $minutes =~ ^[0-9]+$ ]] || ((minutes == 0)); then + echo "Usage: omarchy-reminder [message]" + echo " omarchy-reminder show" + echo " omarchy-reminder clear" + exit 1 +fi + +if [[ -z $message ]]; then + message="Your ${minutes} minutes are up" +fi + +set_at=$(date +%s) +remind_at=$(date -d "+${minutes} minutes" +%H:%M) +unit="omarchy-reminder-${minutes}m-$set_at" +reminder_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-reminders" +message_file="$reminder_dir/$unit.message" +confirmation="You'll be reminded at $remind_at" +confirmation_title="Reminder set for ${minutes} minutes" + +mkdir -p "$reminder_dir" + +if [[ -n $custom_message ]]; then + printf "%s" "$custom_message" >"$message_file" + confirmation_title="$custom_message in ${minutes} minutes" +fi + +systemd-run --user --quiet --collect --on-active="${minutes}m" --unit="$unit" \ + bash -c 'omarchy-notification-send "󰔛" "Reminder" "$1" -u critical; rm -f "$2"' bash "$message" "$message_file" + +omarchy-notification-send "󰔛" "$confirmation_title" "$confirmation" -u low diff --git a/default/hypr/bindings/utilities.conf b/default/hypr/bindings/utilities.conf index c152f884..8bac901a 100644 --- a/default/hypr/bindings/utilities.conf +++ b/default/hypr/bindings/utilities.conf @@ -44,7 +44,12 @@ bindd = SUPER CTRL, PRINT, Extract text (OCR) from screenshot, exec, omarchy-cap bindd = SUPER CTRL, S, Share, exec, omarchy-menu share # Transcoding -bindd = SUPER CTRL, R, Transcode, exec, omarchy-menu transcode +bindd = SUPER CTRL, PERIOD, Transcode, exec, omarchy-menu transcode + +# Reminders +bindd = SUPER CTRL, R, Set reminder, exec, omarchy-menu reminder-set +bindd = SUPER CTRL ALT, R, Show reminders, exec, omarchy-reminder show +bindd = SUPER SHIFT CTRL, R, Clear reminders, exec, omarchy-reminder clear # Waybar-less information bindd = SUPER CTRL ALT, T, Show time, exec, notify-send -u low " $(date +"%A %H:%M · %d %B %Y · Week %V")" diff --git a/default/omarchy-skill/SKILL.md b/default/omarchy-skill/SKILL.md index 5f9d5a7e..8aa5ffaf 100644 --- a/default/omarchy-skill/SKILL.md +++ b/default/omarchy-skill/SKILL.md @@ -6,8 +6,8 @@ description: > ~/.config/alacritty/, ~/.config/foot/, ~/.config/kitty/, ~/.config/ghostty/, ~/.config/mako/, or ~/.config/omarchy/. Triggers: Hyprland, window rules, animations, keybindings, monitors, gaps, borders, blur, opacity, waybar, walker, terminal config, themes, - wallpaper, night light, idle, lock screen, screenshots, layer rules, workspace - settings, display config, and user-facing omarchy commands. Excludes Omarchy + wallpaper, night light, idle, lock screen, screenshots, reminders, layer rules, + workspace settings, display config, and user-facing omarchy commands. Excludes Omarchy source development in ~/.local/share/omarchy/ and `omarchy dev` workflows. --- @@ -30,7 +30,7 @@ It is not for contributing to Omarchy source code. - Layer rules, workspace settings, display/monitor configuration - Themes, wallpapers, fonts, appearance changes - User-facing `omarchy` commands (`omarchy theme ...`, `omarchy refresh ...`, `omarchy restart ...`, etc.) -- Screenshots, screen recording, night light, idle behavior, lock screen +- Screenshots, screen recording, reminders, night light, idle behavior, lock screen **If you're about to edit a config file in ~/.config/ on this system, STOP and use this skill first.** @@ -118,6 +118,7 @@ Run `omarchy --help` for the full list. The most common groups: | `omarchy install` | Install optional software / packages | `omarchy install docker dbs` | | `omarchy launch` | Launch apps | `omarchy launch browser` | | `omarchy capture` | Screenshots and recordings | `omarchy capture screenshot` | +| `omarchy reminder` | Desktop notification reminders | `omarchy reminder 15 "Pickup Jack"` | | `omarchy pkg` | Package management | `omarchy pkg install ` | | `omarchy setup` | Initial setup tasks | `omarchy setup fingerprint` | | `omarchy update` | System updates | `omarchy update` | @@ -358,6 +359,17 @@ When user requests system changes: 5. **Is it a package install?** Use `omarchy install package ` (or `omarchy pkg aur add ` for AUR-only packages) 6. **Unsure if command exists?** Run `omarchy commands` (or `omarchy --help` for one group) +### Reminder Requests + +When the user asks to set a reminder, use `omarchy reminder [message]` directly. Convert natural language durations to minutes and title-case short reminder labels when appropriate. + +```bash +omarchy reminder 15 "Pickup Jack" +omarchy reminder 60 "Check laundry" +omarchy reminder show +omarchy reminder clear +``` + ## Out of Scope This skill intentionally does not cover Omarchy source development. Do not use this skill for: @@ -372,6 +384,9 @@ This skill intentionally does not cover Omarchy source development. Do not use t - "Configure my external monitor" -> Edit `~/.config/hypr/monitors.conf` - "Make the window gaps smaller" -> Edit `~/.config/hypr/looknfeel.conf` - "Set up night light to turn on at sunset" -> `omarchy toggle nightlight` or edit `~/.config/hypr/hyprsunset.conf` +- "Set a reminder to pickup jack in 15 minutes" -> `omarchy reminder 15 "Pickup Jack"` +- "Show my reminders" -> `omarchy reminder show` +- "Clear all reminders" -> `omarchy reminder clear` - "Customize the catppuccin theme colors" -> Create `~/.config/omarchy/themes/catppuccin-custom/` by copying from stock, then edit - "Run a script every time I change themes" -> Create `~/.config/omarchy/hooks/theme-set` - "Reset waybar to defaults" -> `omarchy refresh waybar`