docs: implementation plan for hyprlock migration
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
# Replace gtklock with hyprlock Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Swap the lockscreen from `gtklock` to `hyprlock` so fingerprint unlock works without typing a character first.
|
||||
|
||||
**Architecture:** Replace one tracked config tree (`gtklock/`) with another (`hyprlock/`). Update `packages.txt`, `install.sh`, `niri/config.kdl`, `scripts/enable-fingerprint.sh`, and docs. The user re-runs `install.sh` + `enable-fingerprint.sh` after merge.
|
||||
|
||||
**Tech Stack:** Hyprlang config, niri, bash, PAM.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-05-25-hyprlock-replace-gtklock-design.md`
|
||||
|
||||
**Verification model:** `niri validate` for niri changes, `bash -n` for shell, parse-check for the Hyprlang config (`hyprlock --help` won't validate config without `-c`; spec-correctness is via inspection). Functional end-to-end (touch reader → unlock) is interactive — controller verifies after the live install + bootstrap.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `hyprlock/hyprlock.conf` | New — single Hyprlang file, deployed to `~/.config/hypr/hyprlock.conf` |
|
||||
| `packages.txt` | swap `gtklock` → `hyprlock`, drop `gtklock-userinfo-module` if present |
|
||||
| `install.sh` | drop gtklock symlinks + the `gtklock` mkdir entry, add hyprlock symlink |
|
||||
| `niri/config.kdl` | swap 3 gtklock invocations (Mod+Shift+E + 2 swayidle) |
|
||||
| `scripts/enable-fingerprint.sh` | swap target in `TARGETS` array |
|
||||
| `gtklock/config.ini`, `gtklock/style.css` | deleted |
|
||||
| `README.md`, `ARCHITECTURE.md`, `CLAUDE.md` | swap lockscreen references |
|
||||
|
||||
5 tasks, each atomic.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Add `hyprlock` config + swap `install.sh` + `packages.txt`
|
||||
|
||||
After this task: `hyprlock` is in `packages.txt`, `hyprlock/hyprlock.conf` exists, `install.sh` symlinks it into `~/.config/hypr/` (mkdir-ing the dir if needed), and the gtklock symlinks are gone from `install.sh`. The repo still has `gtklock/` files and niri still calls gtklock — Tasks 2-4 finish the swap.
|
||||
|
||||
**Files:**
|
||||
- Create: `hyprlock/hyprlock.conf`
|
||||
- Modify: `packages.txt`
|
||||
- Modify: `install.sh` (lines 19, 43-44)
|
||||
|
||||
- [ ] **Step 1: Create `hyprlock/hyprlock.conf`**
|
||||
|
||||
```bash
|
||||
mkdir -p hyprlock
|
||||
```
|
||||
|
||||
Write `hyprlock/hyprlock.conf` with this exact content:
|
||||
|
||||
```hyprlang
|
||||
general {
|
||||
grace = 0
|
||||
hide_cursor = true
|
||||
}
|
||||
|
||||
background {
|
||||
monitor =
|
||||
path = screenshot
|
||||
blur_passes = 3
|
||||
blur_size = 8
|
||||
}
|
||||
|
||||
label {
|
||||
monitor =
|
||||
text = cmd[update:1000] date +"%-I:%M %p"
|
||||
font_size = 96
|
||||
color = rgba(c5c8c6ff)
|
||||
position = 0, 150
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
|
||||
label {
|
||||
monitor =
|
||||
text = cmd[update:60000] date +"%A, %B %-d"
|
||||
font_size = 24
|
||||
color = rgba(969896ff)
|
||||
position = 0, 40
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
|
||||
input-field {
|
||||
monitor =
|
||||
size = 300, 50
|
||||
outline_thickness = 2
|
||||
dots_size = 0.25
|
||||
dots_spacing = 0.4
|
||||
inner_color = rgba(282a2eff)
|
||||
outer_color = rgba(81a2beff)
|
||||
font_color = rgba(c5c8c6ff)
|
||||
fail_color = rgba(cc6666ff)
|
||||
check_color = rgba(b5bd68ff)
|
||||
placeholder_text = <i>Touch fingerprint or type password</i>
|
||||
fail_text = <i>$FAIL</i>
|
||||
fade_on_empty = false
|
||||
position = 0, -50
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Update `packages.txt`**
|
||||
|
||||
Currently `gtklock` is on line 13 (between `pamixer` and `swayidle`):
|
||||
```
|
||||
gtklock
|
||||
swayidle
|
||||
```
|
||||
|
||||
Replace `gtklock` with `hyprlock`:
|
||||
```
|
||||
hyprlock
|
||||
swayidle
|
||||
```
|
||||
|
||||
If `gtklock-userinfo-module` appears anywhere in the file, also delete that line. Verify with:
|
||||
```bash
|
||||
grep -E "gtklock" packages.txt
|
||||
```
|
||||
Expected: zero matches after edit.
|
||||
|
||||
```bash
|
||||
grep -c "^hyprlock$" packages.txt
|
||||
```
|
||||
Expected: `1`.
|
||||
|
||||
- [ ] **Step 3: Update `install.sh` — remove gtklock symlinks and entry, add hyprlock symlink**
|
||||
|
||||
Find lines 43-44:
|
||||
```bash
|
||||
ln -sf "$(pwd)/gtklock/config.ini" ~/.config/gtklock/config
|
||||
ln -sf "$(pwd)/gtklock/style.css" ~/.config/gtklock/style.css
|
||||
```
|
||||
|
||||
Replace both lines with:
|
||||
```bash
|
||||
mkdir -p ~/.config/hypr
|
||||
ln -sf "$(pwd)/hyprlock/hyprlock.conf" ~/.config/hypr/hyprlock.conf
|
||||
```
|
||||
|
||||
Also find line 19 (the brace-expansion mkdir):
|
||||
```bash
|
||||
mkdir -p ~/.config/{niri,waybar,wofi,mako,alacritty,gtk-3.0,gtk-4.0,gtklock,fish} ~/.config/environment.d ~/.config/xdg-desktop-portal
|
||||
```
|
||||
|
||||
Remove `gtklock,` from the brace list (no need to mkdir gtklock anymore):
|
||||
```bash
|
||||
mkdir -p ~/.config/{niri,waybar,wofi,mako,alacritty,gtk-3.0,gtk-4.0,fish} ~/.config/environment.d ~/.config/xdg-desktop-portal
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Bash-syntax-check install.sh**
|
||||
|
||||
```bash
|
||||
bash -n install.sh && echo "syntax ok"
|
||||
```
|
||||
|
||||
Expected: `syntax ok`, exit 0.
|
||||
|
||||
- [ ] **Step 5: Confirm changes landed**
|
||||
|
||||
```bash
|
||||
grep -nE "gtklock|hyprlock" install.sh
|
||||
```
|
||||
|
||||
Expected: 2 hits — both for hyprlock (`mkdir -p ~/.config/hypr` and the `ln -sf ... hyprlock.conf` line). No `gtklock` references remain.
|
||||
|
||||
```bash
|
||||
ls hyprlock/hyprlock.conf && wc -l hyprlock/hyprlock.conf
|
||||
```
|
||||
|
||||
Expected: file exists, ~52 lines.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add hyprlock/hyprlock.conf packages.txt install.sh
|
||||
git commit -m "hyprlock: add config + swap install.sh away from gtklock"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Swap niri keybind + swayidle hooks from gtklock to hyprlock
|
||||
|
||||
After this task, niri's lock keybind and swayidle's lock-on-idle/before-sleep hooks all call `hyprlock` instead of `gtklock -d`. The repo still has the `gtklock/` config files and the old PAM target in `enable-fingerprint.sh` — Tasks 3-4 finish those.
|
||||
|
||||
**Files:**
|
||||
- Modify: `niri/config.kdl`
|
||||
|
||||
- [ ] **Step 1: Find the three gtklock invocations**
|
||||
|
||||
```bash
|
||||
grep -n "gtklock" niri/config.kdl
|
||||
```
|
||||
|
||||
Expected: 3 matches — one in the swayidle `spawn-at-startup` line (with two `"gtklock" "-d"` substrings — counts as the same line, may show twice if grep prints once per line), and one keybind line `Mod+Shift+E { spawn "gtklock" "-d"; }`.
|
||||
|
||||
- [ ] **Step 2: Replace the keybind**
|
||||
|
||||
Find `Mod+Shift+E { spawn "gtklock" "-d"; }` and replace with:
|
||||
```kdl
|
||||
Mod+Shift+E { spawn "hyprlock"; }
|
||||
```
|
||||
|
||||
Indentation: 4 spaces, matching the surrounding `binds` block entries.
|
||||
|
||||
- [ ] **Step 3: Replace the swayidle line's two `"gtklock" "-d"` occurrences**
|
||||
|
||||
The swayidle spawn-at-startup line looks like:
|
||||
```kdl
|
||||
spawn-at-startup "swayidle" "-w" "timeout" "300" "niri msg action power-off-monitors" "timeout" "600" "gtklock" "-d" "timeout" "1800" "systemctl suspend-then-hibernate" "before-sleep" "gtklock" "-d"
|
||||
```
|
||||
|
||||
Replace both `"gtklock" "-d"` substrings with `"hyprlock"`. The full line becomes:
|
||||
```kdl
|
||||
spawn-at-startup "swayidle" "-w" "timeout" "300" "niri msg action power-off-monitors" "timeout" "600" "hyprlock" "timeout" "1800" "systemctl suspend-then-hibernate" "before-sleep" "hyprlock"
|
||||
```
|
||||
|
||||
Note: each `"gtklock" "-d"` (two quoted strings, two niri spawn args) becomes a single `"hyprlock"` (one quoted string).
|
||||
|
||||
- [ ] **Step 4: Validate niri config**
|
||||
|
||||
```bash
|
||||
niri validate 2>&1 | tail -2
|
||||
```
|
||||
|
||||
Expected: a final `INFO niri: config is valid`. Exit 0.
|
||||
|
||||
- [ ] **Step 5: Confirm gtklock is gone, hyprlock present**
|
||||
|
||||
```bash
|
||||
grep -c "gtklock" niri/config.kdl
|
||||
grep -c "hyprlock" niri/config.kdl
|
||||
```
|
||||
|
||||
Expected: `0` for gtklock, `3` for hyprlock (one keybind + two swayidle action positions).
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add niri/config.kdl
|
||||
git commit -m "niri: switch lock + idle hooks to hyprlock"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Update `scripts/enable-fingerprint.sh` PAM target
|
||||
|
||||
After this task, re-running the script targets `/etc/pam.d/hyprlock` instead of `/etc/pam.d/gtklock`. The existing line in `/etc/pam.d/gtklock` is left in place on the live system (harmless dead config — see Out of Scope in the spec).
|
||||
|
||||
**Files:**
|
||||
- Modify: `scripts/enable-fingerprint.sh` (one line in the `TARGETS` array)
|
||||
|
||||
- [ ] **Step 1: Find the TARGETS line**
|
||||
|
||||
```bash
|
||||
grep -n "TARGETS" scripts/enable-fingerprint.sh
|
||||
```
|
||||
|
||||
Expected: one line like:
|
||||
```
|
||||
TARGETS=(/etc/pam.d/sudo /etc/pam.d/gtklock)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Replace `gtklock` with `hyprlock`**
|
||||
|
||||
Change the line to:
|
||||
```bash
|
||||
TARGETS=(/etc/pam.d/sudo /etc/pam.d/hyprlock)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Syntax-check**
|
||||
|
||||
```bash
|
||||
bash -n scripts/enable-fingerprint.sh && echo "syntax ok"
|
||||
```
|
||||
|
||||
Expected: `syntax ok`, exit 0.
|
||||
|
||||
- [ ] **Step 4: Confirm the swap**
|
||||
|
||||
```bash
|
||||
grep -n "gtklock\|hyprlock" scripts/enable-fingerprint.sh
|
||||
```
|
||||
|
||||
Expected: one match — the new `hyprlock` line. Zero `gtklock` references.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add scripts/enable-fingerprint.sh
|
||||
git commit -m "scripts: enable-fingerprint targets hyprlock PAM file"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Delete the tracked `gtklock/` directory
|
||||
|
||||
After this task, the repo no longer carries `gtklock/config.ini` or `gtklock/style.css`. The system's `/etc/pam.d/gtklock` (with the harmless dead pam_fprintd line) is untouched. The user's `~/.config/gtklock/` symlinks become broken but harmless since gtklock is no longer launched.
|
||||
|
||||
**Files:**
|
||||
- Delete: `gtklock/config.ini`
|
||||
- Delete: `gtklock/style.css`
|
||||
|
||||
- [ ] **Step 1: Remove the tracked files**
|
||||
|
||||
```bash
|
||||
git rm gtklock/config.ini gtklock/style.css
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
rm 'gtklock/config.ini'
|
||||
rm 'gtklock/style.css'
|
||||
```
|
||||
|
||||
The empty `gtklock/` directory may remain — git ignores empty directories. If it bothers you cosmetically, `rmdir gtklock` to clean up.
|
||||
|
||||
- [ ] **Step 2: Confirm no references remain in the repo (outside docs)**
|
||||
|
||||
```bash
|
||||
grep -rn "gtklock" --exclude-dir=.git --exclude-dir=docs --exclude-dir=.claude /home/alex/Documents/dotfiles
|
||||
```
|
||||
|
||||
Expected: only matches in README.md, ARCHITECTURE.md, CLAUDE.md (those get updated in Task 5). No matches in install.sh, niri/, scripts/, packages.txt, or the deleted `gtklock/` directory.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git commit -m "gtklock: delete tracked config (replaced by hyprlock)"
|
||||
```
|
||||
|
||||
(`git rm` already staged the deletions; no further `git add` needed.)
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Update README, ARCHITECTURE, CLAUDE.md
|
||||
|
||||
After this task, all documentation references `hyprlock` instead of `gtklock`.
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md`
|
||||
- Modify: `ARCHITECTURE.md`
|
||||
- Modify: `CLAUDE.md`
|
||||
|
||||
- [ ] **Step 1: Find every gtklock reference in the three docs**
|
||||
|
||||
```bash
|
||||
grep -n "gtklock" README.md ARCHITECTURE.md CLAUDE.md
|
||||
```
|
||||
|
||||
Expected: a small handful of matches. Most are in tooling tables (`| Lockscreen | gtklock |`) and short narrative mentions.
|
||||
|
||||
- [ ] **Step 2: README.md — replace each gtklock occurrence with hyprlock**
|
||||
|
||||
For each line `grep` returned in README.md, change `gtklock` → `hyprlock`. Most likely:
|
||||
- A tooling table row: `| Lockscreen | gtklock |` → `| Lockscreen | hyprlock |`
|
||||
- The `Mod+Shift+E` mention in the fingerprint setup section, if it referenced gtklock by name (verify against the actual line — it may already be `Mod+Shift+E to lock` without naming the lockscreen)
|
||||
|
||||
For each match, use `sed -i 's/gtklock/hyprlock/g' README.md` if every occurrence should change verbatim, or hand-edit if context requires more thought (rare for this kind of swap).
|
||||
|
||||
Verify after:
|
||||
```bash
|
||||
grep -n "gtklock" README.md
|
||||
```
|
||||
Expected: zero matches.
|
||||
|
||||
```bash
|
||||
grep -c "hyprlock" README.md
|
||||
```
|
||||
Expected: matches the count from Step 1 (1-for-1 replacement).
|
||||
|
||||
- [ ] **Step 3: ARCHITECTURE.md — same swap**
|
||||
|
||||
```bash
|
||||
sed -i 's/gtklock/hyprlock/g' ARCHITECTURE.md
|
||||
grep -n "gtklock" ARCHITECTURE.md
|
||||
```
|
||||
|
||||
Expected: zero matches.
|
||||
|
||||
- [ ] **Step 4: CLAUDE.md — same swap**
|
||||
|
||||
```bash
|
||||
sed -i 's/gtklock/hyprlock/g' CLAUDE.md
|
||||
grep -n "gtklock" CLAUDE.md
|
||||
```
|
||||
|
||||
Expected: zero matches.
|
||||
|
||||
- [ ] **Step 5: Final repo-wide sanity check**
|
||||
|
||||
```bash
|
||||
grep -rn "gtklock" --exclude-dir=.git --exclude-dir=docs --exclude-dir=.claude /home/alex/Documents/dotfiles
|
||||
```
|
||||
|
||||
Expected: zero matches anywhere except inside `docs/` (the historical spec/plan files for the original gtklock setup) and `.claude/` (cached permission entries).
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add README.md ARCHITECTURE.md CLAUDE.md
|
||||
git commit -m "docs: flip lockscreen references to hyprlock"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Final verification (controller, post-merge)
|
||||
|
||||
- [ ] **Install hyprlock**:
|
||||
```bash
|
||||
bash install.sh
|
||||
which hyprlock
|
||||
```
|
||||
Expected: `/usr/bin/hyprlock`.
|
||||
|
||||
- [ ] **Verify config deployed**:
|
||||
```bash
|
||||
ls -la ~/.config/hypr/hyprlock.conf
|
||||
```
|
||||
Expected: symlink to repo's `hyprlock/hyprlock.conf`.
|
||||
|
||||
- [ ] **Re-run PAM bootstrap**:
|
||||
```bash
|
||||
sudo bash scripts/enable-fingerprint.sh
|
||||
```
|
||||
Expected output mentions `/etc/pam.d/hyprlock` got the new line; `/etc/pam.d/sudo` "already present — skipping".
|
||||
|
||||
- [ ] **Verify PAM line is in hyprlock**:
|
||||
```bash
|
||||
grep pam_fprintd /etc/pam.d/hyprlock
|
||||
```
|
||||
Expected: one match.
|
||||
|
||||
- [ ] **Functional tests**:
|
||||
1. `Mod+Shift+E` — locks the screen with clock + date + input field.
|
||||
2. Touch the fingerprint reader (without typing anything) — unlocks immediately.
|
||||
3. Re-lock; type the password instead — also unlocks (fallback).
|
||||
4. Wait 10 min idle — swayidle triggers `hyprlock` automatically.
|
||||
Reference in New Issue
Block a user