diff options
| author | 2026-05-13 13:43:32 +0100 | |
|---|---|---|
| committer | 2026-05-13 13:43:32 +0100 | |
| commit | 5e8df71b02e7cd0d663556cfde60ab8dc8e26f4b (patch) | |
| tree | 4b34ded19fe7880ab482c5c51bba87f80955f229 | |
| parent | 7122e2fced4ed8879442c08b9e97752c4b250f7c (diff) | |
| download | dotfiles-5e8df71b02e7cd0d663556cfde60ab8dc8e26f4b.tar.gz dotfiles-5e8df71b02e7cd0d663556cfde60ab8dc8e26f4b.tar.bz2 dotfiles-5e8df71b02e7cd0d663556cfde60ab8dc8e26f4b.zip | |
feat(mako-history): hide dismissed entries; consolidate keybinds
Two behaviour changes:
- Hide entries that were previously dismissed via the picker (the
state file now acts as a hide-list, not a marker). The list shrinks
as you process it; no more grey ' ' rows clogging the view.
- Drop the secondary Alt-c custom key. wofi's '--define key_custom_N'
appears unreliable past N=0, so Alt-c and Alt-d both silently
failed. Reduce to a single custom slot (Alt-d) and remap actions:
Enter -> copy + dismiss (was: re-emit + mark seen)
Alt-d -> dismiss without copy
Re-emit was rarely useful given mako already shows the bubble on
arrival; copying is the common need.
KEYBINDS.md updated to reflect the new action set.
| -rw-r--r-- | KEYBINDS.md | 80 | ||||
| -rw-r--r-- | dot_config/waybar/executable_mako-history.py | 79 |
2 files changed, 78 insertions, 81 deletions
diff --git a/KEYBINDS.md b/KEYBINDS.md index 59a5db3..efd9b71 100644 --- a/KEYBINDS.md +++ b/KEYBINDS.md @@ -304,46 +304,46 @@ Mod key: `Super` (Mod4). Only personal additions beyond sway defaults listed. ### Personal keybinds (sway/config) -| Key | Action | -| ----------------------- | ----------------------------------------------------------------------------------------- | -| `XF86AudioRaiseVolume` | Volume +5% | -| `XF86AudioLowerVolume` | Volume -5% | -| `XF86AudioMute` | Mute toggle | -| `Super+m` | Mic mute toggle | -| `Super+Shift+m` | Speaker mute toggle | -| `XF86AudioPlay` | Play/pause | -| `XF86AudioNext` | Next track | -| `XF86AudioPrev` | Previous track | -| `XF86MonBrightnessUp` | Brightness +5% (brightnessctl) | -| `XF86MonBrightnessDown` | Brightness -5% (brightnessctl) | -| `XF86AudioMicMute` | Mic mute toggle | -| `XF86Bluetooth` | Bluetooth power toggle (bluetoothctl) | -| `XF86ScreenSaver` | Lock screen + pause media (same as Super+Shift+s) | -| `XF86Sleep` | Suspend system (systemctl suspend) | -| `XF86WLAN` | Toggle Wi-Fi (rfkill) | -| `XF86RFKill` | Toggle all radios (rfkill) | -| `Print` | Region screenshot (grim+slurp) | -| `Shift+Print` | Full screenshot (grim) | -| `Super+i` | Dictate toggle (whisper.cpp → wtype + clipboard) | -| `Super+Shift+o` | OCR region (tesseract → clipboard) | -| `Super+Shift+s` | Lock screen + pause media | -| `Super+n` | Dismiss visible notification (also marks it seen) | -| `Super+Shift+n` | Dismiss all visible notifications (mark all seen) | -| `Super+Ctrl+n` | Restore last dismissed; pop it back into the pending set | -| `XF86Favorites` | Notification history picker (Enter re-shows + marks seen, Alt-c copies, Alt-d marks seen) | -| `Super+p` | Clipboard history picker (wofi; Enter pastes, Alt-d deletes) | -| `Super+Shift+p` | Clipboard history delete entry (Enter deletes) | -| `Super+period` | Emoji picker (bemoji → wofi; types + copies) | -| `Super+Tab` | Next workspace | -| `Super+Shift+Tab` | Previous workspace | -| `Super+]` | Focus next window in container (monocle cycling) | -| `Super+[` | Focus prev window in container (monocle cycling) | -| `XF86Display` | Toggle display mode (laptop-off/side-by-side) | -| `XF86Tools` | Floating pulsemixer (audio mixer TUI) | -| `XF86Keyboard` | Floating glow pager for `~/dotfiles/KEYBINDS.md` | -| `Super+z` then `w` | Display QR for clipboard (wqr) | -| `Super+z` then `r` | Scan QR via webcam, copy to clipboard (rqr) | -| `Super+t` | Toggle Thunderbird (tiled on current workspace) | +| Key | Action | +| ----------------------- | ----------------------------------------------------------------------- | +| `XF86AudioRaiseVolume` | Volume +5% | +| `XF86AudioLowerVolume` | Volume -5% | +| `XF86AudioMute` | Mute toggle | +| `Super+m` | Mic mute toggle | +| `Super+Shift+m` | Speaker mute toggle | +| `XF86AudioPlay` | Play/pause | +| `XF86AudioNext` | Next track | +| `XF86AudioPrev` | Previous track | +| `XF86MonBrightnessUp` | Brightness +5% (brightnessctl) | +| `XF86MonBrightnessDown` | Brightness -5% (brightnessctl) | +| `XF86AudioMicMute` | Mic mute toggle | +| `XF86Bluetooth` | Bluetooth power toggle (bluetoothctl) | +| `XF86ScreenSaver` | Lock screen + pause media (same as Super+Shift+s) | +| `XF86Sleep` | Suspend system (systemctl suspend) | +| `XF86WLAN` | Toggle Wi-Fi (rfkill) | +| `XF86RFKill` | Toggle all radios (rfkill) | +| `Print` | Region screenshot (grim+slurp) | +| `Shift+Print` | Full screenshot (grim) | +| `Super+i` | Dictate toggle (whisper.cpp → wtype + clipboard) | +| `Super+Shift+o` | OCR region (tesseract → clipboard) | +| `Super+Shift+s` | Lock screen + pause media | +| `Super+n` | Dismiss visible notification (also marks it seen) | +| `Super+Shift+n` | Dismiss all visible notifications (mark all seen) | +| `Super+Ctrl+n` | Restore last dismissed; pop it back into the pending set | +| `XF86Favorites` | Notification history picker (Enter copies + dismisses, Alt-d dismisses) | +| `Super+p` | Clipboard history picker (wofi; Enter pastes, Alt-d deletes) | +| `Super+Shift+p` | Clipboard history delete entry (Enter deletes) | +| `Super+period` | Emoji picker (bemoji → wofi; types + copies) | +| `Super+Tab` | Next workspace | +| `Super+Shift+Tab` | Previous workspace | +| `Super+]` | Focus next window in container (monocle cycling) | +| `Super+[` | Focus prev window in container (monocle cycling) | +| `XF86Display` | Toggle display mode (laptop-off/side-by-side) | +| `XF86Tools` | Floating pulsemixer (audio mixer TUI) | +| `XF86Keyboard` | Floating glow pager for `~/dotfiles/KEYBINDS.md` | +| `Super+z` then `w` | Display QR for clipboard (wqr) | +| `Super+z` then `r` | Scan QR via webcam, copy to clipboard (rqr) | +| `Super+t` | Toggle Thunderbird (tiled on current workspace) | ## Typing / Input diff --git a/dot_config/waybar/executable_mako-history.py b/dot_config/waybar/executable_mako-history.py index b072091..3171411 100644 --- a/dot_config/waybar/executable_mako-history.py +++ b/dot_config/waybar/executable_mako-history.py @@ -1,10 +1,14 @@ #!/usr/bin/env python3 """Notification history picker. -Lists mako's history annotated with pending/seen status. Default action -re-emits the notification (so the bubble pops again) and marks it seen. -Alt-c copies "summary\\nbody" to the clipboard via wl-copy. -Alt-d marks the entry seen without re-showing. +Lists pending mako notifications (visible bubbles + history). Entries +previously dismissed via this picker are hidden so the list shrinks as +you process it. + +Keys: + Enter copy "summary\\nbody" to the clipboard and dismiss the entry + Alt-d dismiss without copying + Esc cancel State file: $XDG_RUNTIME_DIR/mako-dismissed (one id per line, per-session). """ @@ -15,6 +19,7 @@ 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" @@ -32,7 +37,7 @@ def _run_makoctl(subcmd: str) -> str: return "" -def _parse_block(out: str) -> list[dict]: +def _parse_block(out: str) -> list[dict[str, Any]]: """Parse mako's text dump. Format (per notification): @@ -43,8 +48,8 @@ def _parse_block(out: str) -> list[dict]: [ <body cont>] Urgency: <urgency> """ - notifs: list[dict] = [] - cur: dict | None = None + notifs: list[dict[str, Any]] = [] + cur: dict[str, Any] | None = None last_field: str | None = None for line in out.splitlines(): m = RECORD_RE.match(line) @@ -63,22 +68,26 @@ def _parse_block(out: str) -> list[dict]: last_field = key continue if last_field == "body" and line.startswith(" "): - cur["body"] = (cur.get("body", "") + " " + line.strip()).strip() + cur["body"] = (str(cur.get("body", "")) + " " + line.strip()).strip() if cur is not None: notifs.append(cur) return notifs -def parse_history() -> list[dict]: - """Return visible + history notifications, deduped by id, visible first.""" +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] = [] + out: list[dict[str, Any]] = [] for n in visible + history: - if n["id"] in seen: + nid = int(n["id"]) + if nid in seen or str(nid) in dismissed: continue - seen.add(n["id"]) + seen.add(nid) out.append(n) return out @@ -91,7 +100,7 @@ def load_dismissed() -> set[str]: 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 "")) + _ = STATE.write_text(payload + ("\n" if ids else "")) def add_dismissed(nid: int) -> None: @@ -100,16 +109,14 @@ def add_dismissed(nid: int) -> None: save_dismissed(ids) -def fmt_line(n: dict, dismissed: set[str]) -> str: - pending = str(n["id"]) not in dismissed - mark = "●" if pending else " " - app = (n.get("app_name") or "?").strip() or "?" - summary = (n.get("summary") or "").strip() - body = (n.get("body") or "").strip() +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"[{mark}] [{app}] {text}\t#{n['id']}" + return f"[{app}] {text}\t#{n['id']}" def parse_selection(line: str) -> int | None: @@ -126,9 +133,7 @@ def run_wofi(input_text: str, lines: int) -> tuple[int, str]: "--prompt", "Notifications", "--define", - "key_custom_0=Alt-c", - "--define", - "key_custom_1=Alt-d", + "key_custom_0=Alt-d", "--lines", str(lines), ] @@ -139,19 +144,20 @@ def run_wofi(input_text: str, lines: int) -> tuple[int, str]: input=input_text, text=True, capture_output=True, + check=False, ) return proc.returncode, proc.stdout.strip() def main() -> None: - notifs = parse_history() dismissed = load_dismissed() + notifs = parse_history(dismissed) if not notifs: - run_wofi("(no notifications)\n", 1) + _ = run_wofi("(no notifications)\n", 1) return - lines_text = "\n".join(fmt_line(n, dismissed) for n in notifs) + "\n" + lines_text = "\n".join(fmt_line(n) for n in notifs) + "\n" rc, sel = run_wofi(lines_text, min(len(notifs), 15)) if not sel or sel.startswith("(no "): @@ -164,23 +170,14 @@ def main() -> None: if notif is None: return - summary = (notif.get("summary") or "").strip() - body = (notif.get("body") or "").strip() - app = (notif.get("app_name") or "").strip() + summary = str(notif.get("summary") or "").strip() + body = str(notif.get("body") or "").strip() clip_text = f"{summary}\n{body}".strip() - if rc == 10: # Alt-c → copy - subprocess.run(["wl-copy"], input=clip_text, text=True) - elif rc == 11: # Alt-d → mark seen, no re-show + if rc == 10: # Alt-d → dismiss without copy add_dismissed(nid) - elif rc == 0: # Enter → re-emit + mark seen - cmd = ["notify-send"] - if app: - cmd += ["-a", app] - cmd.append(summary or "(no summary)") - if body: - cmd.append(body) - subprocess.run(cmd) + elif rc == 0: # Enter → copy + dismiss + _ = subprocess.run(["wl-copy"], input=clip_text, text=True, check=False) add_dismissed(nid) |
