8 Commits

9 changed files with 2033 additions and 159 deletions

View File

@@ -0,0 +1,218 @@
# Hyprland Lua Migration Checklist
This checklist tracks the migration described in `docs/tiling-wm-experience.md`.
Guiding rule for shelling out:
- Prefer Lua for compositor/window/workspace state changes.
- Avoid `hyprctl` for window manipulation unless there is no usable Lua API.
- `hyprctl` remains acceptable for non-window-control escape hatches such as
`hyprctl reload`.
- External utilities remain acceptable where they are the real tool being
launched, for example rofi, cliphist, grim/slurp/swappy, playerctl, hyprlock,
and systemd commands.
## 0. Version And Build Base
- [x] Update/confirm Hyprland Lua input at latest usable upstream target.
- [x] Keep stable Hyprland path intact until Lua path is proven.
- [x] Keep hy3 out of the Lua branch.
- [x] Keep hyprNStack following the Lua Hyprland input.
- [x] Rebuild hyprNStack against the Lua Hyprland branch.
- [x] Add a forked hyprexpo input for the Lua Hyprland branch.
- [x] Keep a cheap Lua check: parse config, execute against stub, reject
`hyprctl` in the Lua config's window/workspace manipulation path.
- [x] Add a real Hyprland Lua verifier check for the config parser path.
Current upstream note: latest Hyprland release observed during this migration is
`v0.54.3`; the Lua config input tracks PR 13817 and was already at the current
PR head `c35a8a5` dated 2026-04-26. The non-Lua fallback remains pinned to the older
hy3/hyprexpo-compatible stack; the Lua branch uses forked hyprexpo branch
`colonelpanic8/hyprland-plugins:hyprexpo-lua-hyprland`.
## 1. Core Layout
- [x] Primary layout is equal-width columns.
- [x] No scrolling layout.
- [x] No hy3 in Lua path.
- [x] Dynamic redistribution on open/close via Lua-managed nStack count.
- [x] Monocle/tabbed-style layout available.
- [x] Direct jump to columns layout.
- [x] Direct jump to monocle layout.
- [x] Directional focus cycles in monocle.
- [x] Visual indication of hidden monocle windows, currently notification.
- [x] Make layout state per workspace instead of one global current layout.
- [x] Preserve one-window smart gaps in the live config path.
- [x] Use a persistent monocle indicator instead of a transient notification.
Smart-gaps note: nStack uses `no_gaps_when_only = true`; Hyprland workspace
rules are still applied at runtime for broader parity, but skipped during
`--verify-config` because the current Lua PR segfaults when rule bindings run in
verifier mode.
## 2. Workspace Behavior
- [x] `Super+1..9` focuses bounded workspaces.
- [x] `Super+Shift+1..9` sends window without following.
- [x] `Super+Ctrl+1..9` sends and follows.
- [x] Previous workspace per monitor uses Lua-tracked history.
- [x] Implement next empty workspace focus in Lua.
- [x] Implement move focused window to next empty workspace without following.
- [x] Implement move focused window to next empty workspace and follow.
- [x] Implement bounded workspace cycling `1..9` in Lua, replacing
`workspace-scroll.sh`.
- [x] Implement workspace swap or decide whether native dispatcher is enough.
- [x] Track current monitor workspace history explicitly, with native
`previous_per_monitor` as fallback.
## 3. Directional Navigation
- [x] `Super+w/a/s/d` focuses windows.
- [x] `Super+Shift+w/a/s/d` swaps windows.
- [x] `Hyper+w/a/s/d` focuses monitors.
- [x] `Hyper+Shift+w/a/s/d` moves windows to monitors.
- [x] `Super+z` next monitor.
- [x] `Super+Shift+z` move to next monitor.
- [x] Replace any old cursor-follow/move scripts fully.
- [x] Add required `Super+Ctrl+w/a/s/d` move-to-monitor behavior preserving
useful focus.
- [x] Add "move to empty workspace on monitor in direction" without requiring
`Hyper+Ctrl`.
- [x] Route directional focus in monocle through deterministic Lua cycling.
- [ ] Live-verify directional focus in monocle behaves predictably.
## 4. Script Elimination Priority
- [x] Core layout switching no longer uses scripts.
- [x] Core column count logic no longer uses scripts or `hyprctl`.
- [x] Replace `find-empty-workspace.sh`.
- [x] Replace `workspace-goto-empty.sh`.
- [x] Replace `workspace-move-to-empty.sh`.
- [x] Replace `workspace-scroll.sh`.
- [x] Replace `cycle-layout.sh`.
- [x] Replace `movewindow-follow-cursor.sh`.
- [x] Replace `gather-class.sh`.
- [x] Replace `focus-next-class.sh`.
- [x] Replace `raise-or-run.sh`.
- [x] Replace minimize scripts if Lua can maintain hidden workspace state.
- [x] Replace `swap-workspaces.sh`.
- [x] Decide whether rofi-backed pickers remain scripts or become
Lua-generated command pipes. Rofi itself remains external.
## 5. Overview And Window Discovery
- [x] Restore visual hyprexpo for `Super+Tab` overview.
- [x] Restore visual hyprexpo `bring` mode for `Super+Shift+Tab`.
- [x] Keep first-pass Lua numbered window picker on secondary bindings.
- [x] Implement first-pass Lua-native go-to-window picker.
- [x] Implement first-pass Lua-native bring-window picker.
- [x] Implement first-pass Lua-native replace-window picker.
- [ ] Picker entries include icons.
- [x] Picker entries include title/workspace.
- [x] Hide scratchpad/minimized/internal windows from normal pickers.
- [x] Decide whether picker data generation can be Lua-native with rofi as only
external process.
Picker decision: current Lua API can query and manipulate windows directly, but
does not expose a synchronous way to run rofi and consume its selected output.
The first pass therefore uses Lua-native numbered submaps and notifications.
A final rofi/icon picker would need either a small IPC bridge or an upstream Lua
process-output/callback primitive.
Hyprexpo decision: hyprexpo is kept as the visual overview. The forked Lua
branch exposes `hl.plugin.hyprexpo.expo(...)`, so the Lua config can invoke
`toggle` and `bring` directly without shelling out to `hyprctl`.
## 6. Scratchpads
- [x] Preserve named scratchpads: element, gmail, htop, messages, slack,
spotify, transmission, volume.
- [x] Preserve dropdown terminal scratchpad.
- [x] Scratchpads near-fullscreen and centered.
- [x] Scratchpads hidden from normal listings/status bar.
- [x] Toggling scratchpad exits fullscreen/monocle state first.
- [x] Decide hyprscratch daemon is not needed in the Lua branch.
- [x] Replace `hyprscratch toggle` with Lua-managed scratchpad toggles.
- [x] Disable hyprscratch service on the Lua branch.
- [x] Handle delayed class/title assignment with window class/title event adoption.
- [x] Handle already-running app.
- [x] Handle minimized app.
- [x] Handle app on another workspace.
## 7. Minimization
- [x] Implement minimize active window.
- [x] Implement restore last minimized window.
- [x] Exclude minimized windows from layout.
- [x] Exclude minimized windows from normal go/bring lists.
- [x] Implement minimized picker.
- [x] Implement restore all minimized.
- [x] Implement minimize other windows of current workspace class.
- [x] Implement restore windows of focused class.
- [x] Decide hidden workspace naming/state model for minimized windows.
- [x] Hydrate minimized-window state from the hidden workspace on restore/picker
paths.
## 8. Class-Aware Workflows
- [x] Gather all windows of focused class onto current workspace.
- [x] Focus next window of different/same class as desired parity.
- [x] Browser raise-or-spawn.
- [x] Window info command exposes class/title/workspace/address/pid.
- [ ] Window menus expose real window icons.
- [x] Prefer Lua window queries over `hyprctl clients`.
## 9. Status Bar Contract
- [ ] Confirm taffybar can still list normal workspaces.
- [ ] Confirm special scratchpad/minimize workspaces are filtered.
- [ ] Confirm active workspace per monitor remains visible.
- [ ] Confirm class/title/active/minimized/urgent metadata is available.
- [x] Expose layout name/state if practical.
- [ ] Confirm workspace/window positioning remains enough for icon strips.
Layout state note: Lua writes `$XDG_RUNTIME_DIR/hyprland-layout-state` with the
active workspace, active layout, and per-workspace layout map. Taffybar still
needs a live readback check.
## 10. Session And Utilities
- [x] Terminal binding preserved.
- [x] Launcher/run menu preserved.
- [x] Media keys preserved.
- [x] Clipboard history binding preserved.
- [x] Screenshot binding preserved.
- [x] Lock binding preserved.
- [x] Session startup target integration preserved.
- [x] `hyprctl reload` may remain available as a non-window-manipulation escape
hatch.
- [x] Resolve `Hyper+w` conflict: monitor focus must win; wallpaper picker
needs another key.
- [x] Keep rofi utility commands as external commands unless there is a
meaningful Lua replacement.
- [x] Decide which shell utilities are acceptable because they are not Hyprland
control scripts.
## 11. Validation
- [x] Lua syntax check.
- [x] Lua stub execution check.
- [x] `hyprctl` rejection in Lua config for window/workspace manipulation.
- [x] Real `Hyprland --verify-config` check.
- [x] hyprNStack flake build check.
- [x] hyprexpo Lua-branch flake build check.
- [x] `ryzen-shine` system dry-run.
- [x] `just switch` activates successfully and deploys branch-owned
`~/.config/hypr/hyprland.lua`.
- [x] Re-run checks after Hyprland/Lua input confirmation.
- [ ] Try live compositor smoke test again after version bump.
- [x] Document `--verify-config` caveats for Lua rule/plugin-specific config.
- [x] Eventually run `just switch` only when the branch is coherent enough for a
live test.
Live-smoke note: this Hyprland binary exposes `--verify-config` but no
`--headless` CLI flag. `just switch` now installs the Lua branch binary and
deploys `hyprland.lua`, but the currently running compositor remains the old
0.53 process until the Hyprland session is restarted. A true compositor smoke
test still needs a session restart or a nested Wayland session that avoids
startup side effects.

View File

@@ -74,6 +74,14 @@ Required behavior:
- Moving the focused window to an empty workspace on the monitor in a direction - Moving the focused window to an empty workspace on the monitor in a direction
remains required behavior, but it should not require an extra `Hyper` remains required behavior, but it should not require an extra `Hyper`
modifier beyond `Shift`. modifier beyond `Shift`.
- `Super+w/a/s/d` focuses windows directionally.
- `Super+Shift+w/a/s/d` swaps or moves the focused window directionally.
- `Super+Ctrl+w/a/s/d` moves the focused window to the monitor in that
direction while preserving useful focus.
- `Super+Ctrl+Shift+w/a/s/d` moves the focused window to an empty workspace on
the monitor in that direction.
- `Hyper+w/a/s/d` focuses monitors directionally.
- `Hyper+Shift+w/a/s/d` swaps or moves windows between monitors directionally.
- Directional focus in tabbed/fullscreen mode should cycle predictably through - Directional focus in tabbed/fullscreen mode should cycle predictably through
windows even though their screen geometry overlaps. windows even though their screen geometry overlaps.
@@ -88,6 +96,7 @@ Required behavior:
- Tiling is dynamic. - Tiling is dynamic.
- Primary layout is equal-width vertical columns. - Primary layout is equal-width vertical columns.
- Scrolling layouts are not acceptable.
- All ordinary splits are vertical. - All ordinary splits are vertical.
- Adding windows dynamically redistributes all tiled windows evenly. - Adding windows dynamically redistributes all tiled windows evenly.
- Removing windows dynamically redistributes all tiled windows evenly. - Removing windows dynamically redistributes all tiled windows evenly.
@@ -235,6 +244,8 @@ Important behavior:
Nice behavior: Nice behavior:
- Wallpaper behavior remains consistent. - Wallpaper behavior remains consistent.
- Wallpaper selection uses `Hyper+comma`; `Hyper+w/a/s/d` are reserved for
directional monitor focus.
- Idle behavior remains consistent. - Idle behavior remains consistent.
- Lock behavior remains consistent. - Lock behavior remains consistent.
- Clipboard history behavior remains consistent. - Clipboard history behavior remains consistent.

View File

@@ -315,12 +315,12 @@ bind = $hyper SHIFT, S, movewindow, mon:d
bind = $hyper SHIFT, A, movewindow, mon:l bind = $hyper SHIFT, A, movewindow, mon:l
bind = $hyper SHIFT, D, movewindow, mon:r bind = $hyper SHIFT, D, movewindow, mon:r
# Shift to empty workspace on screen direction (Hyper + Ctrl + WASD) # Shift to empty workspace on screen direction (Super + Ctrl + Shift + WASD)
# Like XMonad's shiftToEmptyOnScreen # Like XMonad's shiftToEmptyOnScreen
bind = $hyper CTRL, W, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh u bind = $mainMod CTRL SHIFT, W, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh u
bind = $hyper CTRL, S, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh d bind = $mainMod CTRL SHIFT, S, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh d
bind = $hyper CTRL, A, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh l bind = $mainMod CTRL SHIFT, A, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh l
bind = $hyper CTRL, D, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh r bind = $mainMod CTRL SHIFT, D, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh r
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# LAYOUT CONTROL (XMonad-like with hy3) # LAYOUT CONTROL (XMonad-like with hy3)
@@ -527,7 +527,7 @@ bind = $hyper, 9, exec, start_synergy.sh
bind = $hyper, I, exec, rofi_select_input.hs bind = $hyper, I, exec, rofi_select_input.hs
bind = $hyper, backslash, exec, /home/imalison/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle bind = $hyper, backslash, exec, /home/imalison/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle
bind = $hyper, O, exec, rofi_paswitch bind = $hyper, O, exec, rofi_paswitch
bind = $hyper, W, exec, rofi_wallpaper.sh bind = $hyper, comma, exec, rofi_wallpaper.sh
bind = $hyper, Y, exec, rofi_agentic_skill bind = $hyper, Y, exec, rofi_agentic_skill
# Reload config # Reload config

File diff suppressed because it is too large Load Diff

View File

@@ -1012,11 +1012,11 @@ addKeys conf@XConfig { modMask = modm } =
(modm .|. shiftMask) (`windowSwap` True) ++ (modm .|. shiftMask) (`windowSwap` True) ++
buildDirectionalBindings buildDirectionalBindings
(modm .|. controlMask) (followingWindow . (`windowToScreen` True)) ++ (modm .|. controlMask) (followingWindow . (`windowToScreen` True)) ++
buildDirectionalBindings
(modm .|. controlMask .|. shiftMask) shiftToEmptyOnScreen ++
buildDirectionalBindings hyper (`screenGo` True) ++ buildDirectionalBindings hyper (`screenGo` True) ++
buildDirectionalBindings buildDirectionalBindings
(hyper .|. shiftMask) (followingWindow . (`screenSwap` True)) ++ (hyper .|. shiftMask) (followingWindow . (`screenSwap` True)) ++
buildDirectionalBindings
(hyper .|. controlMask) shiftToEmptyOnScreen ++
-- Specific program spawning -- Specific program spawning
bindBringAndRaiseMany bindBringAndRaiseMany
@@ -1101,7 +1101,7 @@ addKeys conf@XConfig { modMask = modm } =
, ((hyper, xK_space), spawn "skippy-xd") , ((hyper, xK_space), spawn "skippy-xd")
, ((hyper, xK_i), spawn "rofi_select_input.hs") , ((hyper, xK_i), spawn "rofi_select_input.hs")
, ((hyper, xK_o), spawn "rofi_paswitch") , ((hyper, xK_o), spawn "rofi_paswitch")
, ((hyper, xK_w), spawn "rofi_wallpaper.sh") , ((hyper, xK_comma), spawn "rofi_wallpaper.sh")
, ((hyper, xK_y), spawn "rofi_agentic_skill") , ((hyper, xK_y), spawn "rofi_agentic_skill")
, ((modm, xK_e), spawn "emacsclient --eval '(emacs-everywhere)'") , ((modm, xK_e), spawn "emacsclient --eval '(emacs-everywhere)'")

255
nixos/flake.lock generated
View File

@@ -335,15 +335,15 @@
"flake-compat_3": { "flake-compat_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1696426674, "lastModified": 1767039857,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "edolstra", "owner": "NixOS",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "edolstra", "owner": "NixOS",
"repo": "flake-compat", "repo": "flake-compat",
"type": "github" "type": "github"
} }
@@ -353,13 +353,13 @@
"locked": { "locked": {
"lastModified": 1767039857, "lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"type": "github" "type": "github"
} }
@@ -367,11 +367,11 @@
"flake-compat_5": { "flake-compat_5": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1767039857, "lastModified": 1696426674,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -502,24 +502,6 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"git-blame-rank": { "git-blame-rank": {
"inputs": { "inputs": {
"fenix": "fenix", "fenix": "fenix",
@@ -685,7 +667,6 @@
"gitignore_3": { "gitignore_3": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"imalison-taffybar",
"taffybar", "taffybar",
"weeder-nix", "weeder-nix",
"pre-commit-hooks", "pre-commit-hooks",
@@ -731,7 +712,7 @@
"hercules-ci-effects_2": { "hercules-ci-effects_2": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_5", "flake-parts": "flake-parts_5",
"nixpkgs": "nixpkgs_7" "nixpkgs": "nixpkgs_6"
}, },
"locked": { "locked": {
"lastModified": 1701009247, "lastModified": 1701009247,
@@ -789,6 +770,29 @@
"type": "github" "type": "github"
} }
}, },
"hyprNStack": {
"inputs": {
"hyprland": [
"hyprland-lua-config"
],
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1777317717,
"narHash": "sha256-Rj4vx0RvEWtnpnizggWRtrGe092bXiGLLt0WijwYWtI=",
"owner": "colonelpanic8",
"repo": "hyprNStack",
"rev": "94607cd53f2ddac88f6b26261393275e7dd590ef",
"type": "github"
},
"original": {
"owner": "colonelpanic8",
"repo": "hyprNStack",
"type": "github"
}
},
"hyprcursor": { "hyprcursor": {
"inputs": { "inputs": {
"hyprlang": [ "hyprlang": [
@@ -1094,6 +1098,37 @@
"type": "github" "type": "github"
} }
}, },
"hyprland-plugins-lua": {
"inputs": {
"hyprland": [
"hyprland-lua-config"
],
"nixpkgs": [
"hyprland-plugins-lua",
"hyprland",
"nixpkgs"
],
"systems": [
"hyprland-plugins-lua",
"hyprland",
"systems"
]
},
"locked": {
"lastModified": 1777413654,
"narHash": "sha256-lVGYGUWf9ynV5lR8QAygAfmYRkko1btIe26UcQ1bGXw=",
"owner": "colonelpanic8",
"repo": "hyprland-plugins",
"rev": "ff36c04b270c26fcd53e623fc688e4eb41672897",
"type": "github"
},
"original": {
"owner": "colonelpanic8",
"ref": "hyprexpo-lua-hyprland",
"repo": "hyprland-plugins",
"type": "github"
}
},
"hyprland-protocols": { "hyprland-protocols": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -1493,7 +1528,9 @@
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
"taffybar": "taffybar", "taffybar": [
"taffybar"
],
"xmonad": [ "xmonad": [
"xmonad" "xmonad"
] ]
@@ -1574,10 +1611,10 @@
}, },
"nix": { "nix": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_4", "flake-compat": "flake-compat_3",
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix", "git-hooks-nix": "git-hooks-nix",
"nixpkgs": "nixpkgs_5", "nixpkgs": "nixpkgs_4",
"nixpkgs-23-11": "nixpkgs-23-11", "nixpkgs-23-11": "nixpkgs-23-11",
"nixpkgs-regression": "nixpkgs-regression" "nixpkgs-regression": "nixpkgs-regression"
}, },
@@ -1634,7 +1671,7 @@
}, },
"nixos-wsl": { "nixos-wsl": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_5", "flake-compat": "flake-compat_4",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
@@ -1768,22 +1805,6 @@
} }
}, },
"nixpkgs_4": { "nixpkgs_4": {
"locked": {
"lastModified": 1776877367,
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0726a0ecb6d4e08f6adced58726b95db924cef57",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1771903837, "lastModified": 1771903837,
"narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=", "narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=",
@@ -1796,7 +1817,7 @@
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz" "url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"
} }
}, },
"nixpkgs_6": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1776877367, "lastModified": 1776877367,
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=", "narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
@@ -1812,7 +1833,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_7": { "nixpkgs_6": {
"locked": { "locked": {
"lastModified": 1697723726, "lastModified": 1697723726,
"narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=", "narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe+/uVNy6zCGMXgoeMb7T9rg=",
@@ -1828,7 +1849,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_8": { "nixpkgs_7": {
"locked": { "locked": {
"lastModified": 1703255338, "lastModified": 1703255338,
"narHash": "sha256-Z6wfYJQKmDN9xciTwU3cOiOk+NElxdZwy/FiHctCzjU=", "narHash": "sha256-Z6wfYJQKmDN9xciTwU3cOiOk+NElxdZwy/FiHctCzjU=",
@@ -1848,7 +1869,7 @@
"inputs": { "inputs": {
"flake-parts": "flake-parts_4", "flake-parts": "flake-parts_4",
"hercules-ci-effects": "hercules-ci-effects_2", "hercules-ci-effects": "hercules-ci-effects_2",
"nixpkgs": "nixpkgs_8", "nixpkgs": "nixpkgs_7",
"osx-kvm": "osx-kvm" "osx-kvm": "osx-kvm"
}, },
"locked": { "locked": {
@@ -2034,10 +2055,9 @@
}, },
"pre-commit-hooks_3": { "pre-commit-hooks_3": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_3", "flake-compat": "flake-compat_5",
"gitignore": "gitignore_3", "gitignore": "gitignore_3",
"nixpkgs": [ "nixpkgs": [
"imalison-taffybar",
"nixpkgs" "nixpkgs"
] ]
}, },
@@ -2115,9 +2135,11 @@
"git-sync-rs": "git-sync-rs", "git-sync-rs": "git-sync-rs",
"home-manager": "home-manager", "home-manager": "home-manager",
"hy3": "hy3", "hy3": "hy3",
"hyprNStack": "hyprNStack",
"hyprland": "hyprland", "hyprland": "hyprland",
"hyprland-lua-config": "hyprland-lua-config", "hyprland-lua-config": "hyprland-lua-config",
"hyprland-plugins": "hyprland-plugins", "hyprland-plugins": "hyprland-plugins",
"hyprland-plugins-lua": "hyprland-plugins-lua",
"hyprscratch": "hyprscratch", "hyprscratch": "hyprscratch",
"imalison-taffybar": "imalison-taffybar", "imalison-taffybar": "imalison-taffybar",
"kanshi-sni": "kanshi-sni", "kanshi-sni": "kanshi-sni",
@@ -2126,15 +2148,16 @@
"nixified-ai": "nixified-ai", "nixified-ai": "nixified-ai",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixos-wsl": "nixos-wsl", "nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_6", "nixpkgs": "nixpkgs_5",
"nixtheplanet": "nixtheplanet", "nixtheplanet": "nixtheplanet",
"notifications-tray-icon": "notifications-tray-icon", "notifications-tray-icon": "notifications-tray-icon",
"org-agenda-api": "org-agenda-api", "org-agenda-api": "org-agenda-api",
"railbird-secrets": "railbird-secrets", "railbird-secrets": "railbird-secrets",
"systems": "systems_4", "systems": "systems_3",
"taffybar": "taffybar",
"vscode-server": "vscode-server", "vscode-server": "vscode-server",
"xmonad": "xmonad_2", "xmonad": "xmonad",
"xmonad-contrib": "xmonad-contrib_2" "xmonad-contrib": "xmonad-contrib"
} }
}, },
"rust-analyzer-src": { "rust-analyzer-src": {
@@ -2236,41 +2259,34 @@
"type": "github" "type": "github"
} }
}, },
"systems_4": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"taffybar": { "taffybar": {
"inputs": { "inputs": {
"flake-utils": "flake-utils_2", "flake-utils": [
"nixpkgs": "nixpkgs_4", "flake-utils"
],
"nixpkgs": [
"nixpkgs"
],
"weeder-nix": "weeder-nix", "weeder-nix": "weeder-nix",
"xmonad": "xmonad", "xmonad": [
"xmonad-contrib": "xmonad-contrib" "xmonad"
],
"xmonad-contrib": [
"xmonad-contrib"
]
}, },
"locked": { "locked": {
"lastModified": 1777396416, "lastModified": 1777401169,
"narHash": "sha256-uuNyU7wO1pSBN7zxC3sCJ4uPmnDgpaW+0lQvJvYEoK8=", "narHash": "sha256-bciN/qFjXYm8ZIKXSc/OssUsLt9GoNs/cU9xT/pw7QY=",
"ref": "refs/heads/master", "owner": "taffybar",
"rev": "ba979b03486c9f27bd67da9cf85152fa49df09ec", "repo": "taffybar",
"revCount": 2291, "rev": "59e3c75990156dcd4353ad9fad5823303e751f0f",
"type": "git", "type": "github"
"url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar"
}, },
"original": { "original": {
"type": "git", "owner": "taffybar",
"url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar" "repo": "taffybar",
"type": "github"
} }
}, },
"vscode-server": { "vscode-server": {
@@ -2299,7 +2315,6 @@
"weeder-nix": { "weeder-nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"imalison-taffybar",
"taffybar", "taffybar",
"nixpkgs" "nixpkgs"
], ],
@@ -2402,7 +2417,20 @@
} }
}, },
"xmonad": { "xmonad": {
"flake": false, "inputs": {
"flake-utils": [
"flake-utils"
],
"git-ignore-nix": [
"git-ignore-nix"
],
"nixpkgs": [
"nixpkgs"
],
"unstable": [
"nixpkgs"
]
},
"locked": { "locked": {
"lastModified": 1776502138, "lastModified": 1776502138,
"narHash": "sha256-mSOpNU1iJvfFh5uwayA6aPxneFMduNW1kG1gV2tGE+c=", "narHash": "sha256-mSOpNU1iJvfFh5uwayA6aPxneFMduNW1kG1gV2tGE+c=",
@@ -2413,29 +2441,11 @@
}, },
"original": { "original": {
"owner": "xmonad", "owner": "xmonad",
"ref": "master",
"repo": "xmonad", "repo": "xmonad",
"type": "github" "type": "github"
} }
}, },
"xmonad-contrib": { "xmonad-contrib": {
"flake": false,
"locked": {
"lastModified": 1769258911,
"narHash": "sha256-YGEKXs4UmS5QOIELJTdCiMzTktuue+Bd3yFoIKSHuBU=",
"owner": "xmonad",
"repo": "xmonad-contrib",
"rev": "803bc3d12bdcc512ec06856c4f119d37de1ba338",
"type": "github"
},
"original": {
"owner": "xmonad",
"ref": "master",
"repo": "xmonad-contrib",
"type": "github"
}
},
"xmonad-contrib_2": {
"inputs": { "inputs": {
"flake-utils": [ "flake-utils": [
"flake-utils" "flake-utils"
@@ -2464,35 +2474,6 @@
"repo": "xmonad-contrib", "repo": "xmonad-contrib",
"type": "github" "type": "github"
} }
},
"xmonad_2": {
"inputs": {
"flake-utils": [
"flake-utils"
],
"git-ignore-nix": [
"git-ignore-nix"
],
"nixpkgs": [
"nixpkgs"
],
"unstable": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1776502138,
"narHash": "sha256-mSOpNU1iJvfFh5uwayA6aPxneFMduNW1kG1gV2tGE+c=",
"owner": "xmonad",
"repo": "xmonad",
"rev": "a618fb32662e44eb5d8276a3dc1925b0233e638b",
"type": "github"
},
"original": {
"owner": "xmonad",
"repo": "xmonad",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@@ -101,6 +101,14 @@
url = "git+https://github.com/hyprwm/Hyprland?submodules=1&ref=refs/pull/13817/head"; url = "git+https://github.com/hyprwm/Hyprland?submodules=1&ref=refs/pull/13817/head";
}; };
hyprNStack = {
url = "github:colonelpanic8/hyprNStack";
inputs = {
hyprland.follows = "hyprland-lua-config";
nixpkgs.follows = "nixpkgs";
};
};
hy3 = { hy3 = {
url = "github:outfoxxed/hy3?ref=hl0.53.0"; url = "github:outfoxxed/hy3?ref=hl0.53.0";
inputs.hyprland.follows = "hyprland"; inputs.hyprland.follows = "hyprland";
@@ -111,6 +119,11 @@
inputs.hyprland.follows = "hyprland"; inputs.hyprland.follows = "hyprland";
}; };
hyprland-plugins-lua = {
url = "github:colonelpanic8/hyprland-plugins?ref=hyprexpo-lua-hyprland";
inputs.hyprland.follows = "hyprland-lua-config";
};
hyprscratch = { hyprscratch = {
url = "github:colonelpanic8/hyprscratch/reapply-rules-on-toggle"; url = "github:colonelpanic8/hyprscratch/reapply-rules-on-toggle";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -151,7 +164,7 @@
taffybar = { taffybar = {
# Use a remote, lockfile-pinned input so rebuilds are reproducible across # Use a remote, lockfile-pinned input so rebuilds are reproducible across
# machines. For local development, use `nixos-rebuild --override-input taffybar path:...`. # machines. For local development, use `nixos-rebuild --override-input taffybar path:...`.
url = "github:taffybar/taffybar?ref=codex/fix-gdk-backend-strut-detection"; url = "github:taffybar/taffybar";
inputs = { inputs = {
nixpkgs.follows = "nixpkgs"; nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils"; flake-utils.follows = "flake-utils";
@@ -467,6 +480,7 @@
} // flake-utils.lib.eachDefaultSystem (system: } // flake-utils.lib.eachDefaultSystem (system:
let let
pkgs = import nixpkgs { inherit system; }; pkgs = import nixpkgs { inherit system; };
lib = pkgs.lib;
# Get short revs for tagging # Get short revs for tagging
orgApiRev = builtins.substring 0 7 (org-agenda-api.rev or "unknown"); orgApiRev = builtins.substring 0 7 (org-agenda-api.rev or "unknown");
@@ -487,6 +501,149 @@
packages = { packages = {
colonelpanic-org-agenda-api = containerLib.containers.colonelpanic; colonelpanic-org-agenda-api = containerLib.containers.colonelpanic;
kat-org-agenda-api = containerLib.containers.kat; kat-org-agenda-api = containerLib.containers.kat;
} // lib.optionalAttrs pkgs.stdenv.isLinux {
hyprNStack = inputs.hyprNStack.packages.${system}.hyprNStack;
hyprexpo-lua = inputs.hyprland-plugins-lua.packages.${system}.hyprexpo;
};
checks = lib.optionalAttrs pkgs.stdenv.isLinux {
hyprNStack = inputs.hyprNStack.packages.${system}.hyprNStack;
hyprexpo-lua = inputs.hyprland-plugins-lua.packages.${system}.hyprexpo;
hyprland-lua-config-syntax = pkgs.runCommand "hyprland-lua-config-syntax" {
nativeBuildInputs = [ pkgs.lua5_4 ];
} ''
luac -p ${../dotfiles/config/hypr/hyprland.lua}
if grep -n 'hyprctl' ${../dotfiles/config/hypr/hyprland.lua} | grep -v 'hyprctl reload'; then
echo "hyprland.lua should not shell out to hyprctl for window/workspace manipulation" >&2
exit 1
fi
lua <<'LUA'
local callbacks = {}
local function noop() end
local function dispatcher_proxy()
local proxy = {}
return setmetatable(proxy, {
__index = function()
return dispatcher_proxy()
end,
__call = function()
return noop
end,
})
end
local notification = {
is_alive = function()
return true
end,
set_text = noop,
set_timeout = noop,
pause = noop,
resume = noop,
set_paused = noop,
dismiss = noop,
}
local monitor = {
id = 1,
name = "stub-monitor",
focused = true,
}
local workspace = {
id = 1,
name = "1",
windows = 0,
special = false,
monitor = monitor,
}
monitor.active_workspace = workspace
hl = {
animation = noop,
bind = noop,
config = noop,
curve = noop,
env = noop,
exec_cmd = noop,
define_submap = function(_, reset_or_callback, callback)
local cb = type(reset_or_callback) == "function" and reset_or_callback or callback
if cb then
cb()
end
end,
monitor = noop,
workspace_rule = noop,
window_rule = noop,
dsp = dispatcher_proxy(),
notification = {
create = function()
return notification
end,
},
plugin = {
load = noop,
},
get_active_workspace = function()
return workspace
end,
get_active_monitor = function()
return monitor
end,
get_active_window = function()
return nil
end,
get_monitor = function()
return monitor
end,
get_workspace = function(id)
if tostring(id) == "1" then
return workspace
end
return nil
end,
get_windows = function()
return {}
end,
get_workspace_windows = function()
return {}
end,
on = function(_, callback)
callbacks[#callbacks + 1] = callback
end,
timer = function(callback)
callback()
return {
set_enabled = noop,
}
end,
}
dofile("${../dotfiles/config/hypr/hyprland.lua}")
for _, callback in ipairs(callbacks) do
callback()
end
LUA
touch "$out"
'';
hyprland-lua-verify-config = let
hyprlandPackage = inputs.hyprland-lua-config.packages.${system}.hyprland;
hyprNStackPackage = inputs.hyprNStack.packages.${system}.hyprNStack;
in pkgs.runCommand "hyprland-lua-verify-config" {} ''
cp ${../dotfiles/config/hypr/hyprland.lua} hyprland.lua
substituteInPlace hyprland.lua \
--replace-fail /run/current-system/sw/lib/libhyprNStack.so \
${hyprNStackPackage}/lib/libhyprNStack.so
export XDG_RUNTIME_DIR="$TMPDIR/runtime"
mkdir -p "$XDG_RUNTIME_DIR"
HYPRLAND_NO_CRASHREPORTER=1 ${pkgs.coreutils}/bin/timeout 20s \
${hyprlandPackage}/bin/Hyprland --verify-config --config "$PWD/hyprland.lua"
touch "$out"
'';
}; };
# Dev shell for org-agenda-api deployment # Dev shell for org-agenda-api deployment

View File

@@ -7,6 +7,10 @@ let
if cfg.useLuaConfigBranch if cfg.useLuaConfigBranch
then inputs.hyprland-lua-config then inputs.hyprland-lua-config
else inputs.hyprland; else inputs.hyprland;
luaPluginPackages = lib.optionals cfg.useLuaConfigBranch [
inputs.hyprNStack.packages.${system}.hyprNStack
inputs.hyprland-plugins-lua.packages.${system}.hyprexpo
];
hyprexpoPatched = inputs.hyprland-plugins.packages.${system}.hyprexpo.overrideAttrs (old: { hyprexpoPatched = inputs.hyprland-plugins.packages.${system}.hyprexpo.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ patches = (old.patches or [ ]) ++ [
./patches/hyprexpo-pr-612-workspace-numbers.patch ./patches/hyprexpo-pr-612-workspace-numbers.patch
@@ -35,7 +39,15 @@ let
home-manager.sharedModules = [ home-manager.sharedModules = [
inputs.hyprscratch.homeModules.default inputs.hyprscratch.homeModules.default
{ ({ config, ... }: {
xdg.configFile."hypr" = {
force = true;
source =
if cfg.useLuaConfigBranch
then ../dotfiles/config/hypr
else config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/dotfiles/dotfiles/config/hypr";
};
services.kanshi = { services.kanshi = {
enable = true; enable = true;
systemdTarget = "graphical-session.target"; systemdTarget = "graphical-session.target";
@@ -78,8 +90,8 @@ let
]; ];
}; };
programs.hyprscratch = { programs.hyprscratch = {
enable = true; enable = !cfg.useLuaConfigBranch;
settings = { settings = {
daemon_options = "clean"; daemon_options = "clean";
global_options = ""; global_options = "";
@@ -135,7 +147,7 @@ let
}; };
}; };
}; };
} })
]; ];
# Hyprland-specific packages # Hyprland-specific packages
@@ -157,10 +169,10 @@ let
# For scripts # For scripts
jq jq
] ++ lib.optionals enableExternalPluginPackages [ ] ++ luaPluginPackages ++ lib.optionals enableExternalPluginPackages [
# External plugin packages are pinned to the stable 0.53 stack. # External plugin packages are pinned to the stable 0.53 stack.
# PR 13817's Hyprland branch builds, but hy3 / hyprexpo do not yet build # Keep hy3 on the stable stack; the Lua branch uses hyprNStack and the
# against it, so keep them out of the experimental Lua branch for now. # forked Lua-compatible hyprexpo input instead.
inputs.hy3.packages.${system}.hy3 inputs.hy3.packages.${system}.hy3
hyprexpoPatched hyprexpoPatched
]; ];
@@ -173,11 +185,10 @@ enabledModule // {
default = false; default = false;
description = '' description = ''
Use the experimental Hyprland PR 13817 Lua-config branch for the Use the experimental Hyprland PR 13817 Lua-config branch for the
Hyprland package itself. Third-party plugins are left on the stable Hyprland package itself. The experimental package set excludes hy3, and
stack and are excluded from the experimental package set because current includes the Lua-branch builds of hyprNStack and hyprexpo instead. When
`hy3` and `hyprexpo` sources do not build against PR 13817 yet. The a sibling `hyprland.lua` is present, the Lua config manager picks it
existing `hyprland.conf` remains active until a sibling `hyprland.lua` before `hyprland.conf`.
file is added.
''; '';
}; };
}; };

View File

@@ -9,6 +9,7 @@
features.full.enable = true; features.full.enable = true;
myModules.kubelet.enable = false; myModules.kubelet.enable = false;
myModules.nvidia.enable = true; myModules.nvidia.enable = true;
myModules.hyprland.useLuaConfigBranch = true;
# Needed for now because monitors have different refresh rates # Needed for now because monitors have different refresh rates
myModules.xmonad.picom.vSync.enable = false; myModules.xmonad.picom.vSync.enable = false;
myModules.cache-server = { myModules.cache-server = {