Commit Graph

137 Commits

Author SHA1 Message Date
funman300 286bb8a3b3 spec: native niri scratchpad (replaces workspace-based minimize)
Design for a Sway-style scratchpad built into niri as a pinned patch set +
niri-scratchpad PKGBUILD. Stores hidden windows in a global stash on Layout
rather than a named workspace, removing the +1 offset and multi-monitor bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 11:09:52 -07:00
funman300 b8a858d7a7 niri: working minimize via declared stash workspace (#1)
Mod+M/Mod+Shift+M referenced an undeclared 'minimized' workspace and were silent no-ops. Declare the stash, add focus=false, and add the window-restore wofi picker.
2026-07-16 17:19:57 +00:00
funman300 05cc026d75 fix(window-restore): re-read workspace/window state after the wofi pick
The picker blocks on user input for however long they take. In that
window niri can destroy a dynamic workspace (its last window closed)
and renumber every later one down, so the pre-pick current["idx"] can
point at the wrong workspace by the time move-window-to-workspace
runs — restoring to the wrong place. The stashed window itself can
also have closed in the meantime, in which case the move/focus calls
would silently no-op. main() now re-reads workspaces/windows after
pick() returns, recomputes the current workspace, and confirms the
chosen window still exists before acting; each bails with a notify
and the documented exit code otherwise. Adds window_exists() as a
pure, tested helper alongside the existing pure functions.

Also: clarify the focus=false comment in niri/config.kdl (it read as
if focus=false caused the problem it actually prevents), and note in
the design doc's Out of scope section that the stash's index-1
pinning is per-output, so a second monitor would need its own
analysis of the Mod+1..9 offset.
2026-07-16 10:10:40 -07:00
funman300 41f0ee88ff niri: working minimize via declared stash workspace
Mod+M referenced an undeclared 'minimized' workspace, so it silently did
nothing. Declare it, add focus=false so focus does not follow the window
into the stash, and point Mod+Shift+M at the restore picker.

niri pins declared named workspaces to index 1 and will not move them, so
Mod+1..9 are offset by one.
2026-07-16 10:03:12 -07:00
funman300 5979eb229f window-restore: wofi picker to restore minimized windows
Pure logic split from the niri/wofi calls so it unit-tests without a
compositor. Restores to the current workspace, so no origin tracking.
2026-07-16 09:58:25 -07:00
funman300 9ae1d88937 plan: niri minimize/stash implementation
Two tasks: window-restore.py + unit tests, then the niri config declaration,
bind rewiring and install.sh symlink. Script precedes config so Mod+Shift+M is
never wired to a missing script.

Plan code verified by extracting both blocks and running the test suite against
the script: 18/18 pass, and the step-2 failure mode is confirmed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:19:49 -07:00
funman300 1bdc4b5165 spec: niri minimize/stash design
Mod+M and Mod+Shift+M reference a "minimized" named workspace that was never
declared, so both binds have always been silent no-ops. Design covers the
declaration plus a wofi restore picker.

Constraints verified against niri 26.04 in a nested instance: undeclared named
workspaces are no-ops, declared ones are pinned to index 1 and cannot be moved
(hence the +1 offset on Mod+1..9), and focus=false is required or focus follows
the window into the stash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 09:15:02 -07:00
funman300 ff2ffe47b6 niri: arrow-key variants for focus and move binds
Only the H/J/K/L letters were bound, so Mod+Shift+Left and friends did
nothing. Add arrow equivalents next to the existing letter binds for both
focusing and moving; the letter binds are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 08:57:56 -07:00
funman300 2e168053c6 volume: keep the OSD visible in gamemode and fullscreen
Two effects were hiding the volume slider:

- gamemode-session turns on mako's do-not-disturb mode, whose invisible=1
  suppressed every notification, the OSD included.
- niri draws fullscreen windows above the top layer, mako's default, so the
  OSD rendered underneath fullscreen windows even outside gamemode.

Tag the OSD with its own app name so mako can match it, then exempt just that
name: invisible=0 to survive DND, layer=overlay to clear fullscreen. The block
is listed after [mode=do-not-disturb] because later criteria win in mako.

Verified with DND active and a fullscreen window: the OSD shows, while another
app's notification under the same conditions stays suppressed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 08:48:41 -07:00
funman300 3d08abb060 gamemode-watch: single-instance flock guard
Prevents a manually-launched watcher and the spawn-at-startup one from
racing the same winwatch source; the second instance exits immediately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:54:17 -07:00
funman300 0ab08e8561 gamemode: global auto-trigger via niri window watch
Add scripts/gamemode-watch.py, launched at niri startup, which subscribes
to niri's event stream and toggles gamemode whenever a game window
(steam_app_*, RuneLite, gamescope) is open — so Steam games trigger
gamemode with zero per-game setup instead of needing gamemoderun %command%.

'winwatch' is a third reference-counted source alongside feral + manual.
Pure Watcher class is unit-tested; SIGTERM/finally guarantee cleanup so
the watcher stopping never leaves gamemode stuck on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:51:52 -07:00
funman300 98f6c56fe3 install.sh: link gamemode-session, gamemode.ini, and mako/config
Adds the new gamemode wiring to the installer; also links mako/config,
which was never linked before (it used to be gitignored).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:39:58 -07:00
funman300 1fd160f19d niri: Mod+G gamemode toggle; drop dead VRR rules
The eDP-1 panel has no VRR, so the three variable-refresh-rate rules
(including the incorrect is-focused=true one) were no-ops. Removed;
left a breadcrumb comment for a future external VRR monitor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:38:13 -07:00
funman300 259de11868 gamemode.ini: Feral custom hooks drive gamemode-session
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:37:40 -07:00
funman300 2c9cff6bb9 gamemode-session: discard effect-command stdout
makoctl/fw-fanctrl echo confirmations to stdout that leaked into add/del
output during integration. run() now discards stdout, keeps stderr.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:37:40 -07:00
funman300 77b2866e3d mako: track config, add do-not-disturb mode for gamemode
Un-ignore mako/config and bring it under dotfiles so the gamemode
do-not-disturb mode syncs across machines. [mode=do-not-disturb]
invisible=1 suppresses popups while retaining notification history.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:35:01 -07:00
funman300 4b1773ac4e gamemode-session: real effects (idle, DND, perf+fan, waybar)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:32:32 -07:00
funman300 05707afc04 gamemode-session: reference-counted state machine core
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:31:52 -07:00
funman300 e0c12ff0c9 Plan: niri gamemode implementation (6 tasks, TDD)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:29:44 -07:00
funman300 5b24b21610 Spec: niri gamemode (idle inhibit, DND, perf profile, hide waybar)
Hybrid trigger (Feral gamemode hooks + Mod+G toggle), reference-counted
so overlapping sources apply once and revert once. Also removes three
dead VRR window-rules (panel has no VRR support).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:23:34 -07:00
funman300 6e26cc62aa niri: drop Steam Big Picture fullscreen rule
Big Picture reuses the existing client window (title change only), so the
open-fullscreen rule never fires on toggle; unused, removing it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 10:04:19 -07:00
funman300 400d81ff73 niri: fullscreen Steam Big Picture mode
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 09:59:27 -07:00
funman300 dc810ed2a6 niri: float and center Steam client and child windows
Add window rules for the Steam client (app-id "steam"): all client
windows float and center, the main library window opens large (75x85%),
and notification toasts sit unobtrusively in the bottom-right without
border or focus-ring. Launched games (steam_app_) are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 09:59:01 -07:00
funman300 88f2131bdd docs: document hypridle, correct stale swww->awww references
Adds the hypridle entry to ARCHITECTURE, and folds in the pending
swww->awww wallpaper-tool rename in ARCHITECTURE + README (the repo spawns
awww-daemon, not swww).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 14:04:23 -07:00
funman300 95d4bf612b feat(waybar): remove system tray module
Drops 'tray' from modules-right and its config block. Network and
bluetooth status remain as their own dedicated modules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 14:04:23 -07:00
funman300 961733e127 feat: replace swayidle with hypridle for idle management
hypridle pairs with hyprlock, honors idle inhibitors (video/calls no
longer blank or lock the screen), and is config-file driven. Same staging
as before: blank monitors @5m, lock @10m, suspend-then-hibernate @30m.
Locks via 'loginctl lock-session' so the before-sleep lock is deduplicated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 14:04:23 -07:00
funman300 1e3adbc842 feat: centralize theme palette + add waybar bluetooth module
Theme centralization:
- theme/colors.json is now the single source of truth for the palette.
- New theme/generate-theme.py renders, at install time, the three derived
  artifacts into ~/.config: colors.css (waybar/wofi @import), mako config,
  and hyprlock-colors.conf (hyprlang $tn_* variables).
- theme/colors.css removed from the repo (now generated); install.sh calls
  the generator instead of the old inline mako snippet.
- hyprlock.conf sources the generated color file and uses $tn_* variables
  instead of hardcoded rgba() values.
- Generator covered by theme/test_generate_theme.py.

Bluetooth:
- Added waybar built-in bluetooth module (config.jsonc + style.css), with
  bluez/bluez-utils in packages.txt.

Includes design spec and implementation plan under docs/superpowers/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 16:45:01 -07:00
funman300 5305bb78c6 scripts: also wire fingerprint into greetd login
Adds /etc/pam.d/greetd to the TARGETS array so re-running
enable-fingerprint.sh extends fprintd auth to the regreet login
screen. Cold-boot timing and silent-touch UX caveats documented
in the spec are accepted as-is — fingerprint failure falls back to
password, no risk of lockout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:45:05 -07:00
funman300 2ca47c02a4 hyprlock: enable fingerprint auth path
hyprlock doesn't poll fprintd by default — pam_authenticate() is
only invoked on password submission. Adding the auth.fingerprint
block tells hyprlock to launch fprintd polling in parallel with the
password input, so touching the reader unlocks immediately without
typing first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:40:32 -07:00
funman300 28f6160b02 hyprlock: pin label font + flesh out ARCHITECTURE description
Final-review caveats:
- ARCHITECTURE.md hyprlock section was a generic one-liner inherited
  from gtklock; now mentions fprintd integration + GPU + blur.
- hyprlock label blocks didn't set font_family; clock/date now
  pinned to JetBrains Mono Nerd Font (same as rest of desktop).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:28:04 -07:00
funman300 577c169bae docs: flip lockscreen references to hyprlock
Replace all gtklock mentions in README, ARCHITECTURE, CLAUDE.md with
hyprlock, and drop the stale gtklock consumer note from theme/colors.css
header comment (hyprlock uses Hyprlang, not CSS imports).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:19:49 -07:00
funman300 ee93bcb7e8 gtklock: delete tracked config (replaced by hyprlock)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:18:29 -07:00
funman300 2fcfc62cdd scripts: enable-fingerprint targets hyprlock PAM file
Replace /etc/pam.d/gtklock with /etc/pam.d/hyprlock in the TARGETS
array now that hyprlock is the session locker. Update the header
comment to match.
2026-05-25 11:17:10 -07:00
funman300 c3bcf19e97 niri: switch lock + idle hooks to hyprlock
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:15:14 -07:00
funman300 bc3333a82a hyprlock: add config + swap install.sh away from gtklock
Adds hyprlock/hyprlock.conf with Tomorrow Night colors, replaces gtklock
with hyprlock in packages.txt, and updates install.sh to symlink
hyprlock.conf into ~/.config/hypr/ instead of the old gtklock symlinks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:12:49 -07:00
funman300 467be569fd docs: implementation plan for hyprlock migration
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:10:50 -07:00
funman300 d7ab4baf7e docs: spec for hyprlock migration (replaces gtklock)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:08:38 -07:00
funman300 30a300014a docs: document one-time fingerprint setup 2026-05-25 10:45:24 -07:00
funman300 b34d4269af scripts: add enable-fingerprint.sh (one-time PAM bootstrap)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:33:00 -07:00
funman300 17272611ea packages: add fprintd for fingerprint reader support 2026-05-25 10:31:02 -07:00
funman300 ede1399853 docs: implementation plan for fingerprint reader
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:30:08 -07:00
funman300 2cd28edf52 docs: spec for Framework 13 fingerprint reader setup
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:28:50 -07:00
funman300 622b0ef8b8 docs: document one-time NetworkManager activation 2026-05-13 14:53:12 -07:00
funman300 d5ad517673 niri: re-add nm-applet --indicator spawn-at-startup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:51:20 -07:00
funman300 bfb30d0a4f packages: also track networkmanager
Discovered while re-adding network-manager-applet that the
networkmanager daemon was already installed locally but never tracked
in packages.txt. Track it now so a fresh install gets both the daemon
and the applet, making the README's NetworkManager activation step
work end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:49:46 -07:00
funman300 33c11e328f packages: re-add network-manager-applet (now functional)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:49:03 -07:00
funman300 9362a5ee1d NetworkManager: track wifi-backend.conf and deploy via install.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:46:50 -07:00
funman300 b57d134c2b docs: implementation plan for NetworkManager activation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:44:57 -07:00
funman300 101accc26b docs: spec for NetworkManager activation with iwd backend
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:43:10 -07:00
funman300 996f11d333 drop snixembed: waybar provides its own SNI watcher
snixembed registered as the StatusNotifierWatcher but its
RegisterStatusNotifierItem implementation didn't accept SNI publishers
— Telegram, Vesktop, blueman-tray, and a libayatana-appindicator test
script all failed to register, leaving waybar's tray empty.

Removing snixembed lets waybar's tray module register itself as both
host AND watcher (when no other watcher exists). After this change,
all three SNI items registered immediately.

Trade-off: legacy XEmbed-based Wine tray icons no longer bridge to
SNI. The Wine System Tray host window rule is kept (still hides the
empty window off-screen) and the comment updated.

- niri/config.kdl: drop snixembed spawn-at-startup; update Wine rule
- packages.txt: drop snixembed (no longer used)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:10:44 -07:00