Lua helpers for stacked named special workspaces on Hyprland: multiple layers per numeric workspace (special:sp4_1, special:sp4_2, …) instead of a single .
made to satifsy this random idea "i have seen the vision, a infront scrollin window manager you have your regular workspaces but you can open infinite amounts of topworkspaces on top of the current one by adding a mod key to switch to a specific later depending on the active workspace say your on active workspace 8 but you want to go on toped workspace 6 so you hit super+shift+6 and boom your now ontop of your current workspace 6 top workspaces removed"
Copy or symlink this directory somewhere stable, shi idk e.g.:
mkdir -p ~/.config/hypr/scripts
cp -r layerstacked ~/.config/hypr/scripts/
# or
ln -s /path/to/repo/layerstacked ~/.config/hypr/scripts/layerstackedScripts resolve hypr.lua / state.lua from their own directory, so keep the folder intact.
- Path:
$XDG_STATE_HOME/hypr_layerstack/state.lua, or
~/.local/state/hypr_layerstack/state.luaifXDG_STATE_HOMEis unset. - Holds
stack(last viewed depth per base workspace id; kept when you close the overlay sotoggle_layerreopens the same depth) andwindow_home(per-window return targets fortoggle_window.lua).
| Option | Suggested | Why |
|---|---|---|
binds:hide_special_on_workspace_change |
true |
Stops specials from following you when you change workspace with Super+1 etc. |
misc:close_special_on_empty |
false |
Empty layers stay defined; you open them with binds anyway. |
| Script | Arguments | What it does |
|---|---|---|
toggle_layer.lua |
— | On a layer → workspace <base> to drop overlay; on base → workspace special:…. Same workspace dispatcher approach as navigate.lua. |
toggle_window.lua |
— | On a layer → move window to base and remember sp… in window_home. On base with memory → restore. On base without memory → send to sp{base}_{stack[base] or 1}. |
navigate.lua |
up | down | deeper | shallower | <n> |
Change view via workspace special:… / workspace <id> (not togglespecialworkspace; see README section below). Prefer deeper / shallower if up / down keys misbind. |
send_to_layer.lua |
<n> |
movetoworkspacesilent special:sp{base}_{n}. Optional follow — edit FOLLOW_AFTER_SEND at top of the file. |
send_up_down.lua |
up | down |
On a layer → move window one level up/down. On base (normal workspace) → up sends to sp{base}_{stack[base] or 1}; down no-op. |
Naming: layers use special:sp{workspace_id}_{depth} (e.g. base workspace 4, layer 2 → special:sp4_2).
hyprctl dispatch togglespecialworkspace takes the short name (sp4_2).
movetoworkspace / movetoworkspacesilent use the full special:sp4_2 form.
Use the directory that contains the .lua files (adjust the path):
L=~/.config/hypr/scripts/layerstacked
lua "$L/navigate.lua" up
lua "$L/navigate.lua" down
lua "$L/navigate.lua" deeper
lua "$L/navigate.lua" shallower
lua "$L/navigate.lua" 3
lua "$L/toggle_layer.lua"
lua "$L/toggle_window.lua"
lua "$L/send_to_layer.lua" 2
lua "$L/send_up_down.lua" up
lua "$L/send_up_down.lua" downExecutable bit + shebang:
chmod +x "$L"/*.lua
"$L/toggle_layer.lua"Set L to your real path (no trailing slash required if you use …/layerstacked/toggle_layer.lua).
$L = ~/.config/hypr/scripts/layerstacked
# Same muscle memory as a simple scratch: view toggle + window stash
bind = $mod, S, exec, lua $L/toggle_layer.lua
bind = $mod SHIFT, S, exec, lua $L/toggle_window.lua
# Navigate view — prefer deeper/shallower if up/down binds are ignored (Hypr key-name quirk)
bind = $mod CTRL, bracketright, exec, lua $L/navigate.lua up
bind = $mod CTRL, bracketleft, exec, lua $L/navigate.lua down
# bind = $mod CTRL, up, exec, lua $L/navigate.lua up
# bind = $mod CTRL, down, exec, lua $L/navigate.lua down
bind = $mod CTRL, 1, exec, lua $L/navigate.lua 1
bind = $mod CTRL, 2, exec, lua $L/navigate.lua 2
bind = $mod CTRL, 3, exec, lua $L/navigate.lua 3
# Send focused window to layer n (silent unless you enable FOLLOW_AFTER_SEND)
bind = $mod CTRL SHIFT, 1, exec, lua $L/send_to_layer.lua 1
bind = $mod CTRL SHIFT, 2, exec, lua $L/send_to_layer.lua 2
# Nudge window between layers all the way down to base
bind = $mod ALT, bracketleft, exec, lua $L/send_up_down.lua down
bind = $mod ALT, bracketright, exec, lua $L/send_up_down.lua upWith uwsm (wrap only if you need the systemd session env):
bind = $mod, S, exec, uwsm app -- lua ~/.config/hypr/scripts/layerstacked/toggle_layer.luaEdit send_to_layer.lua:
local FOLLOW_AFTER_SEND = trueThen a dispatch also runs togglespecialworkspace sp{base}_{n} so your view jumps to that layer.
hyprctl dispatch togglespecialworkspace sp999test
hyprctl dispatch togglespecialworkspace sp999test2
hyprctl activewindow -j | jq -r '.workspace.name' # expect special:sp999test2Hyprland’s togglespecialworkspace closes the special if that exact special is already the active one on the monitor. So “go deeper” from layer 1 with wrong depth detection, or any re-dispatch of the same name, looks like a toggle off instead of staying open or stepping to another layer.
navigate.lua and toggle_layer.lua therefore use hyprctl dispatch workspace special:sp{n}_{d} to switch layers (Hyprland calls setSpecialWorkspace — swap without that toggle bug) and workspace <base_id> to drop back to the numeric base workspace (works with binds:hide_special_on_workspace_change).
View toggles and navigation resolve from the focused monitor’s specialWorkspace.name and active workspace in hyprctl monitors -j, not from activewindow.workspace.*.
If they used activewindow, a fast cross-monitor switch can still leave the previously focused window as Hyprland’s active window for a moment. Then a view-only bind like toggle_layer.lua can reopen sp5_2 on the newly focused monitor while you are already on workspace 3 there.
Window-moving scripts still use the active window where that is the correct subject.
Hyprland allows at most 97 named special workspaces at once — stay within that for total sp* names in use.