diff options
| author | 2026-05-13 13:43:42 +0100 | |
|---|---|---|
| committer | 2026-05-13 13:43:42 +0100 | |
| commit | 26ceab690dff09d5162443b14adccfbe0e9bcd5d (patch) | |
| tree | acfd10c910e1a73c47d4be118348163705dae57d /dot_config | |
| parent | ae464450eb68fb154181823d059cf7124258ad2c (diff) | |
| download | dotfiles-26ceab690dff09d5162443b14adccfbe0e9bcd5d.tar.gz dotfiles-26ceab690dff09d5162443b14adccfbe0e9bcd5d.tar.bz2 dotfiles-26ceab690dff09d5162443b14adccfbe0e9bcd5d.zip | |
refactor(notifications): drop dismissed-state machinery; pending = visible
Set mako default-timeout=0 so notifications stay until acted upon. With
auto-timeout off, mako's list IS the pending set, so the
$XDG_RUNTIME_DIR/mako-dismissed bridge becomes dead weight.
- mako/config: default-timeout=0; drop redundant [urgency=critical]
default-timeout=0 override.
- Delete dismiss-visible.sh and restore-pending.sh; sway calls makoctl
directly (Mod+n=dismiss, Mod+Shift+n=dismiss --all,
Mod+Ctrl+n=restore as undo).
- Shrink mako-status.sh to a 20-line counter of makoctl list.
- Rename mako-history.py -> notification-picker.py; lists only
visible, dismisses via makoctl dismiss -n <id>.
- Update waybar config.jsonc on-click path.
- Update KEYBINDS.md wording (no more 'marks seen' / 'pending set').
Diffstat (limited to 'dot_config')
| -rw-r--r-- | dot_config/mako/config | 3 | ||||
| -rw-r--r-- | dot_config/sway/config | 12 | ||||
| -rw-r--r-- | dot_config/waybar/config.jsonc | 2 | ||||
| -rw-r--r-- | dot_config/waybar/executable_dismiss-visible.sh | 40 | ||||
| -rw-r--r-- | dot_config/waybar/executable_mako-status.sh | 55 | ||||
| -rw-r--r-- | dot_config/waybar/executable_notification-picker.py (renamed from dot_config/waybar/executable_mako-history.py) | 83 | ||||
| -rw-r--r-- | dot_config/waybar/executable_restore-pending.sh | 23 |
7 files changed, 29 insertions, 189 deletions
diff --git a/dot_config/mako/config b/dot_config/mako/config index 87f06bf..fe2d84d 100644 --- a/dot_config/mako/config +++ b/dot_config/mako/config @@ -7,7 +7,7 @@ border-radius=0 padding=8 margin=4 width=300 -default-timeout=5000 +default-timeout=0 anchor=top-right max-history=50 @@ -19,7 +19,6 @@ border-color=#fabd2f [urgency=critical] border-color=#fb4934 -default-timeout=0 [mode=do-not-disturb] invisible=1 diff --git a/dot_config/sway/config b/dot_config/sway/config index efd7561..18b402c 100644 --- a/dot_config/sway/config +++ b/dot_config/sway/config @@ -186,9 +186,9 @@ bindsym $mod+Shift+o exec ~/.local/bin/ocr bindsym $mod+Shift+s exec "playerctl -a pause; swaylock -f -e -c 000000" # Notifications -bindsym $mod+n exec ~/.config/waybar/dismiss-visible.sh top -bindsym $mod+Shift+n exec ~/.config/waybar/dismiss-visible.sh all -bindsym $mod+Ctrl+n exec ~/.config/waybar/restore-pending.sh +bindsym $mod+n exec makoctl dismiss +bindsym $mod+Shift+n exec makoctl dismiss --all +bindsym $mod+Ctrl+n exec makoctl restore # Clipboard history bindsym $mod+p exec ~/.config/waybar/clip-picker.sh pick @@ -203,7 +203,7 @@ bindsym --no-repeat XF86Display exec ~/.config/sway/display-toggle.sh # Multimedia hardware keys (uncommon) bindsym XF86Tools exec $term --class=floating -e pulsemixer bindsym XF86Keyboard exec $term --class=floating -e glow -p ~/dotfiles/KEYBINDS.md -bindsym XF86Favorites exec ~/.config/waybar/mako-history.py +bindsym XF86Favorites exec ~/.config/waybar/notification-picker.py # Quick scratch tools bindsym $mod+Shift+t exec $term --class=floating -e sh -c 'nvim "$XDG_RUNTIME_DIR/scratch-$(date +%s).txt"' @@ -221,7 +221,7 @@ bindsym $mod+z mode "qr" # System / hardware submode — non-XF86 alternatives for the rarely used # hardware toggles and TUI launchers above. Enter with Super+x; mnemonic # letters (b=bluetooth, w=wifi, r=rfkill, s=sleep, d=display, -# v=volume-mixer, k=keybinds, m=mako-history). +# v=volume-mixer, k=keybinds, m=notification-picker). mode "system" { bindsym b exec ~/.config/sway/bt-toggle.sh, mode "default" bindsym w exec ~/.config/waybar/wifi-toggle.sh, mode "default" @@ -230,7 +230,7 @@ mode "system" { bindsym d exec ~/.config/sway/display-toggle.sh, mode "default" bindsym v exec $term --class=floating -e pulsemixer, mode "default" bindsym k exec $term --class=floating -e glow -p ~/dotfiles/KEYBINDS.md, mode "default" - bindsym m exec ~/.config/waybar/mako-history.py, mode "default" + bindsym m exec ~/.config/waybar/notification-picker.py, mode "default" bindsym n exec makoctl mode -t do-not-disturb, mode "default" bindsym Escape mode "default" bindsym Return mode "default" diff --git a/dot_config/waybar/config.jsonc b/dot_config/waybar/config.jsonc index a5bada7..e5ea09b 100644 --- a/dot_config/waybar/config.jsonc +++ b/dot_config/waybar/config.jsonc @@ -209,7 +209,7 @@ "exec": "~/.config/waybar/mako-status.sh", "return-type": "json", "interval": 2, - "on-click": "~/.config/waybar/mako-history.py", + "on-click": "~/.config/waybar/notification-picker.py", "on-click-right": "makoctl dismiss --all", "on-click-middle": "makoctl restore", "tooltip": true, diff --git a/dot_config/waybar/executable_dismiss-visible.sh b/dot_config/waybar/executable_dismiss-visible.sh deleted file mode 100644 index 0f4f987..0000000 --- a/dot_config/waybar/executable_dismiss-visible.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh -# Dismiss currently-visible mako notifications and record their ids in the -# shared "dismissed" set so they don't linger as pending in waybar. -# -# Usage: dismiss-visible.sh [top|all] (default: top) -# -# Coordinates with mako-status.sh and mako-history.py via -# $XDG_RUNTIME_DIR/mako-dismissed (one id per line, per-session). - -set -eu - -mode=${1:-top} -state=${XDG_RUNTIME_DIR:-/tmp}/mako-dismissed -mkdir -p "$(dirname "$state")" -: >>"$state" - -command -v makoctl >/dev/null 2>&1 || exit 0 - -# This makoctl has no -f; extract ids from the text dump. -list_ids() { - makoctl list 2>/dev/null | - sed -n 's/^Notification \([0-9][0-9]*\):.*/\1/p' -} - -case "$mode" in - top) - id=$(list_ids | head -n1 || true) - [ -n "${id:-}" ] && printf '%s\n' "$id" >>"$state" - makoctl dismiss - ;; - all) - list_ids >>"$state" || true - makoctl history 2>/dev/null | sed -n 's/^Notification \([0-9][0-9]*\):.*/\1/p' >>"$state" || true - makoctl dismiss --all - ;; - *) - echo "usage: $0 [top|all]" >&2 - exit 2 - ;; -esac diff --git a/dot_config/waybar/executable_mako-status.sh b/dot_config/waybar/executable_mako-status.sh index 12e4d82..4e9f053 100644 --- a/dot_config/waybar/executable_mako-status.sh +++ b/dot_config/waybar/executable_mako-status.sh @@ -1,56 +1,21 @@ #!/bin/sh -# Waybar status: count of *pending* notifications, where pending = ids in -# mako's history/list that have NOT been explicitly dismissed by the user -# via Mod+n / Mod+Shift+n / the history picker. -# -# State file: $XDG_RUNTIME_DIR/mako-dismissed (per-session, plain id list). +# Waybar status: count of currently-visible mako notifications. With +# default-timeout=0 in mako/config, "visible" == "pending"; once a +# notification is dismissed it's gone and never comes back. set -eu if ! command -v makoctl >/dev/null 2>&1; then - printf '{"text":"","tooltip":"mako not installed","class":"off"} -' + printf '{"text":"","tooltip":"mako not installed","class":"off"}\n' exit 0 fi -state=${XDG_RUNTIME_DIR:-/tmp}/mako-dismissed -: >>"$state" +count=$(makoctl list 2>/dev/null | + grep -c '^Notification [0-9][0-9]*:' || true) -# This makoctl has no -f flag; parse the text dump. Each notification -# starts with "Notification N: <summary>". Visible bubbles live in -# `list`, closed ones in `history`; their id-spaces are disjoint. -extract_ids() { - makoctl "$1" 2>/dev/null | - sed -n 's/^Notification \([0-9][0-9]*\):.*/\1/p' -} - -all_ids=$({ - extract_ids list - extract_ids history -} | sort -un) - -# Prune stale ids (no longer present in mako) from the dismissed file. -if [ -s "$state" ] && [ -n "$all_ids" ]; then - tmp=$(mktemp) - printf '%s -' "$all_ids" >"$tmp.all" - grep -Fxf "$tmp.all" "$state" >"$tmp" 2>/dev/null || : - mv "$tmp" "$state" - rm -f "$tmp.all" -fi - -if [ -z "$all_ids" ]; then - pending=0 -else - pending=$(printf '%s -' "$all_ids" | grep -Fxvf "$state" | grep -c . || true) -fi - -if [ "$pending" -gt 0 ]; then - printf '{"text":" %s","tooltip":"%s pending","class":"pending"} -' \ - "$pending" "$pending" +if [ "$count" -gt 0 ]; then + printf '{"text":" %s","tooltip":"%s pending","class":"pending"}\n' \ + "$count" "$count" else - printf '{"text":"","tooltip":"no pending notifications","class":"empty"} -' + printf '{"text":"","tooltip":"no pending notifications","class":"empty"}\n' fi diff --git a/dot_config/waybar/executable_mako-history.py b/dot_config/waybar/executable_notification-picker.py index a8104f7..e6d6aed 100644 --- a/dot_config/waybar/executable_mako-history.py +++ b/dot_config/waybar/executable_notification-picker.py @@ -1,53 +1,30 @@ #!/usr/bin/env python3 -"""Notification history picker. +"""Notification picker. -Lists pending mako notifications (visible bubbles + history). Entries -dismissed via this picker are hidden so the list shrinks as you process it. -The picker re-opens after each selection so multiple notifications can be -processed in one go; Esc closes it. - -Keys: - Enter copy "summary\\nbody" to the clipboard, dismiss the entry, reopen - Esc close the picker - -State file: $XDG_RUNTIME_DIR/mako-dismissed (one id per line, per-session). +Lists currently-visible mako notifications. Selecting one copies its +"summary\\nbody" to the clipboard and dismisses it via `makoctl dismiss +-n <id>`. The picker re-opens after each selection so multiple +notifications can be processed in one go; Esc closes it. """ from __future__ import annotations -import os import re import subprocess from pathlib import Path from typing import Any -STATE = Path(os.environ.get("XDG_RUNTIME_DIR", "/tmp")) / "mako-dismissed" - - RECORD_RE = re.compile(r"^Notification (\d+):\s?(.*)$") FIELD_RE = re.compile(r"^ ([A-Za-z][A-Za-z ]*?):\s*(.*)$") -def _run_makoctl(subcmd: str) -> str: +def list_notifications() -> list[dict[str, Any]]: try: - return subprocess.run( - ["makoctl", subcmd], capture_output=True, text=True, check=True + out = subprocess.run( + ["makoctl", "list"], capture_output=True, text=True, check=True ).stdout except (subprocess.CalledProcessError, FileNotFoundError): - return "" - - -def _parse_block(out: str) -> list[dict[str, Any]]: - """Parse mako's text dump. - - Format (per notification): - Notification N: <summary> - App name: <app> - [Category: <cat>] - [Body: <body line>] - [ <body cont>] - Urgency: <urgency> - """ + return [] notifs: list[dict[str, Any]] = [] cur: dict[str, Any] | None = None last_field: str | None = None @@ -74,48 +51,12 @@ def _parse_block(out: str) -> list[dict[str, Any]]: return notifs -def parse_history(dismissed: set[str]) -> list[dict[str, Any]]: - """Return visible + history notifications, deduped by id, visible first. - - Entries whose id is in `dismissed` are filtered out. - """ - visible = _parse_block(_run_makoctl("list")) - history = _parse_block(_run_makoctl("history")) - seen: set[int] = set() - out: list[dict[str, Any]] = [] - for n in visible + history: - nid = int(n["id"]) - if nid in seen or str(nid) in dismissed: - continue - seen.add(nid) - out.append(n) - return out - - -def load_dismissed() -> set[str]: - STATE.parent.mkdir(parents=True, exist_ok=True) - STATE.touch(exist_ok=True) - return {x for x in STATE.read_text().split() if x} - - -def save_dismissed(ids: set[str]) -> None: - payload = "\n".join(sorted(ids, key=lambda s: int(s) if s.isdigit() else 0)) - _ = STATE.write_text(payload + ("\n" if ids else "")) - - -def add_dismissed(nid: int) -> None: - ids = load_dismissed() - ids.add(str(nid)) - save_dismissed(ids) - - def fmt_line(n: dict[str, Any]) -> str: app = (str(n.get("app_name") or "?")).strip() or "?" summary = str(n.get("summary") or "").strip() body = str(n.get("body") or "").strip() text = summary if not body else f"{summary} — {body}" text = text.replace("\t", " ").replace("\r", " ") - # Trailing id sentinel for parsing on selection. return f"[{app}] {text}\t#{n['id']}" @@ -145,9 +86,7 @@ def run_wofi(input_text: str, lines: int) -> str: def main() -> None: while True: - dismissed = load_dismissed() - notifs = parse_history(dismissed) - + notifs = list_notifications() if not notifs: _ = run_wofi("(no notifications)\n", 1) return @@ -168,7 +107,7 @@ def main() -> None: body = str(notif.get("body") or "").strip() clip_text = f"{summary}\n{body}".strip() _ = subprocess.run(["wl-copy"], input=clip_text, text=True, check=False) - add_dismissed(nid) + _ = subprocess.run(["makoctl", "dismiss", "-n", str(nid)], check=False) if __name__ == "__main__": diff --git a/dot_config/waybar/executable_restore-pending.sh b/dot_config/waybar/executable_restore-pending.sh deleted file mode 100644 index 3a08c4f..0000000 --- a/dot_config/waybar/executable_restore-pending.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# Restore the most recently closed mako notification and remove its id -# from the dismissed-set so it counts as pending again. - -set -eu - -state=${XDG_RUNTIME_DIR:-/tmp}/mako-dismissed -: >>"$state" - -command -v makoctl >/dev/null 2>&1 || exit 0 - -# mako's history is most-recent-first; the next restore() target is the -# top of the list at the time of the call. -top_id=$(makoctl history 2>/dev/null | - sed -n 's/^Notification \([0-9][0-9]*\):.*/\1/p' | - head -n1 || true) -makoctl restore || true - -if [ -n "${top_id:-}" ] && [ -s "$state" ]; then - tmp=$(mktemp) - grep -Fxv "$top_id" "$state" >"$tmp" || : - mv "$tmp" "$state" -fi |
