216 lines
7.5 KiB
Markdown
216 lines
7.5 KiB
Markdown
|
|
# Hyprland Lua Migration Guide for AI Agents
|
||
|
|
|
||
|
|
## Objective
|
||
|
|
Migrate an existing legacy Hyprland configuration (using `.conf` files based on the old `hyprlang` syntax) to the new Lua-based configuration system introduced in Hyprland v0.55+.
|
||
|
|
|
||
|
|
**CRITICAL INSTRUCTION FOR THE AGENT:**
|
||
|
|
The target machine has a slightly different configuration (e.g., different monitors, workspaces, applications, keybindings) than the original machine this guide was generated on. **Do NOT blindly copy-paste the examples below.** Use them *only* as a translation reference to convert the exact values found on the target machine into the new Lua syntax.
|
||
|
|
|
||
|
|
## Migration Principles
|
||
|
|
1. **Modularity**: Lua uses `require("module_name")` for imports instead of `source = ...`. A file `bindings.lua` is imported via `require("bindings")`. Subdirectories work via dot-notation: `require("bindings.media")` maps to `bindings/media.lua`.
|
||
|
|
2. **Extensions**: Create new `.lua` files mirroring the names of the old `.conf` files.
|
||
|
|
3. **Scope**: Only Hyprland config files are migrated to Lua (e.g., `hyprland.conf`, `monitors.conf`, `bindings.conf`). Do **not** migrate configurations for external tools like `hypridle.conf`, `hyprlock.conf`, `waybar`, or `hyprpaper`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Syntax Translation Reference
|
||
|
|
|
||
|
|
### 1. Variables and Environment Variables
|
||
|
|
**Legacy (.conf):**
|
||
|
|
```hyprlang
|
||
|
|
$terminal = kitty
|
||
|
|
env = GDK_SCALE,1
|
||
|
|
```
|
||
|
|
**Lua (.lua):**
|
||
|
|
```lua
|
||
|
|
local terminal = "kitty"
|
||
|
|
hl.env("GDK_SCALE", "1")
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Autostart (`exec-once`)
|
||
|
|
**Legacy:**
|
||
|
|
```hyprlang
|
||
|
|
exec-once = uwsm app -- waybar
|
||
|
|
exec-once = uwsm app -- hyprpaper
|
||
|
|
```
|
||
|
|
**Lua:**
|
||
|
|
```lua
|
||
|
|
hl.on("hyprland.start", function ()
|
||
|
|
hl.exec_cmd("uwsm app -- waybar")
|
||
|
|
hl.exec_cmd("uwsm app -- hyprpaper")
|
||
|
|
end)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Monitors
|
||
|
|
**Legacy:**
|
||
|
|
```hyprlang
|
||
|
|
monitor=eDP-1,1920x1080,auto,1
|
||
|
|
monitor=desc:Xiaomi Corporation Mi Monitor,3440x1440@60,auto,1
|
||
|
|
```
|
||
|
|
**Lua:**
|
||
|
|
```lua
|
||
|
|
hl.monitor({ output = "eDP-1", mode = "1920x1080", position = "auto", scale = 1 })
|
||
|
|
hl.monitor({ output = "desc:Xiaomi Corporation Mi Monitor", mode = "3440x1440@60", position = "auto", scale = 1 })
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Configuration Blocks (`general`, `decoration`, etc.)
|
||
|
|
**Legacy:**
|
||
|
|
```hyprlang
|
||
|
|
general {
|
||
|
|
gaps_in = 4
|
||
|
|
col.active_border = rgba(33ccffee)
|
||
|
|
layout = dwindle
|
||
|
|
}
|
||
|
|
decoration {
|
||
|
|
rounding = 4
|
||
|
|
blur {
|
||
|
|
enabled = true
|
||
|
|
size = 8
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
**Lua:**
|
||
|
|
```lua
|
||
|
|
hl.config({
|
||
|
|
general = {
|
||
|
|
gaps_in = 4,
|
||
|
|
col = {
|
||
|
|
active_border = "rgba(33ccffee)",
|
||
|
|
},
|
||
|
|
layout = "dwindle",
|
||
|
|
},
|
||
|
|
decoration = {
|
||
|
|
rounding = 4,
|
||
|
|
blur = {
|
||
|
|
enabled = true,
|
||
|
|
size = 8,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Keybindings (`bind`, `bindd`, `bindm`, `bindl`, `binde`)
|
||
|
|
**Legacy:**
|
||
|
|
```hyprlang
|
||
|
|
bind = SUPER, Q, killactive,
|
||
|
|
bindd = SUPER, B, Launch Browser, exec, firefox
|
||
|
|
bind = SUPER, left, movefocus, l
|
||
|
|
bind = SUPER, 1, workspace, 1
|
||
|
|
bindm = SUPER, mouse:272, movewindow
|
||
|
|
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
|
||
|
|
bind = SUPER, code:10, workspace, 1
|
||
|
|
bind = SUPER SHIFT, S, swapwindow, l
|
||
|
|
```
|
||
|
|
**Lua:**
|
||
|
|
Flags from the legacy format (`e` for repeat, `l` for locked, `m` for mouse) become booleans in the options table. Dispatchers must use the `hl.dsp.*` API. **If a dispatcher isn't listed below, fall back to `hl.dsp.exec_cmd("hyprctl dispatch <dispatcher> <args>")`**.
|
||
|
|
```lua
|
||
|
|
hl.bind("SUPER + Q", hl.dsp.window.close())
|
||
|
|
hl.bind("SUPER + B", hl.dsp.exec_cmd("firefox"), { description = "Launch Browser" })
|
||
|
|
hl.bind("SUPER + left", hl.dsp.focus({ direction = "left" }))
|
||
|
|
hl.bind("SUPER + 1", hl.dsp.focus({ workspace = 1 }))
|
||
|
|
|
||
|
|
hl.bind("SUPER + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
||
|
|
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true, locked = true })
|
||
|
|
|
||
|
|
-- Keycodes use the `code:` prefix directly in the string
|
||
|
|
hl.bind("SUPER + code:10", hl.dsp.focus({ workspace = 1 }))
|
||
|
|
|
||
|
|
-- Fallback for undocumented or complex dispatchers
|
||
|
|
hl.bind("SUPER + SHIFT + S", hl.dsp.exec_cmd("hyprctl dispatch swapwindow l"))
|
||
|
|
```
|
||
|
|
*Note: Key combinations are joined by ` + `.*
|
||
|
|
*Common Dispatchers translation:*
|
||
|
|
- `killactive` -> `hl.dsp.window.close()`
|
||
|
|
- `togglefloating` -> `hl.dsp.window.float({ action = "toggle" })`
|
||
|
|
- `fullscreen, 0` -> `hl.dsp.window.fullscreen(0)`
|
||
|
|
- `togglesplit` -> `hl.dsp.layout("togglesplit")`
|
||
|
|
- `movefocus, l` -> `hl.dsp.focus({ direction = "left" })`
|
||
|
|
- `workspace, 1` -> `hl.dsp.focus({ workspace = 1 })`
|
||
|
|
- `movetoworkspace, 1` -> `hl.dsp.window.move({ workspace = 1 })`
|
||
|
|
- `cyclenext` -> `hl.dsp.exec_cmd("hyprctl dispatch cyclenext")`
|
||
|
|
|
||
|
|
### 6. Window and Layer Rules
|
||
|
|
**Legacy:**
|
||
|
|
```hyprlang
|
||
|
|
layerrule = blur true, match:namespace waybar
|
||
|
|
windowrule = opacity 0.85 0.8,match:class ^chrome-gemini\.google\.com.*
|
||
|
|
windowrule = float true,match:class ^org\.gnome\.Calculator$
|
||
|
|
windowrule = no_focus true,match:class ^$,match:title ^$,match:xwayland true
|
||
|
|
```
|
||
|
|
**Lua:**
|
||
|
|
*Watch out for string escaping in regex! Single backslashes in regex must become double backslashes in Lua strings.* Multi-matches are grouped into the `match` table.
|
||
|
|
```lua
|
||
|
|
hl.layer_rule({ match = { namespace = "waybar" }, blur = true })
|
||
|
|
|
||
|
|
hl.window_rule({ match = { class = "^chrome-gemini\\.google\\.com.*" }, opacity = "0.85 0.8" })
|
||
|
|
hl.window_rule({ match = { class = "^org\\.gnome\\.Calculator$" }, float = true })
|
||
|
|
hl.window_rule({
|
||
|
|
match = { class = "^$", title = "^$", xwayland = true },
|
||
|
|
no_focus = true
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7. Advanced Features (Animations, Devices, Gestures)
|
||
|
|
**Legacy Animations:**
|
||
|
|
```hyprlang
|
||
|
|
bezier = easeOutQuint, 0.23, 1, 0.32, 1
|
||
|
|
animation = windows, 1, 4.79, easeOutQuint
|
||
|
|
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
|
||
|
|
```
|
||
|
|
**Lua Animations:**
|
||
|
|
*Note: Speed 1 in legacy = 100ms in lua. 4.79 -> 4.79.*
|
||
|
|
```lua
|
||
|
|
hl.curve("easeOutQuint", { type = "bezier", points = {{0.23, 1}, {0.32, 1}} })
|
||
|
|
|
||
|
|
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" })
|
||
|
|
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" })
|
||
|
|
```
|
||
|
|
|
||
|
|
**Legacy Devices & Gestures:**
|
||
|
|
```hyprlang
|
||
|
|
device {
|
||
|
|
name = epic-mouse-v1
|
||
|
|
sensitivity = -0.5
|
||
|
|
}
|
||
|
|
gesture = 3, horizontal, workspace
|
||
|
|
```
|
||
|
|
**Lua Devices & Gestures:**
|
||
|
|
```lua
|
||
|
|
hl.device({
|
||
|
|
name = "epic-mouse-v1",
|
||
|
|
sensitivity = -0.5
|
||
|
|
})
|
||
|
|
|
||
|
|
hl.gesture({
|
||
|
|
fingers = 3,
|
||
|
|
direction = "horizontal",
|
||
|
|
action = "workspace"
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Validation Steps (Post-Migration)
|
||
|
|
|
||
|
|
Once the agent has translated the `.conf` files into `.lua` files, it MUST perform the following validations before declaring the task complete:
|
||
|
|
|
||
|
|
1. **Syntax Check**: Use the built-in Lua compiler to ensure there are no missing brackets or syntax errors.
|
||
|
|
```bash
|
||
|
|
luac -p ~/.config/hypr/hyprland.lua
|
||
|
|
# Run this for every .lua file generated
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Restart Notification**: Inform the user that because the configuration language is shifting from `.conf` to `.lua`, Hyprland will only detect this on a fresh startup. The user MUST log out and log back in to activate the new configuration.
|
||
|
|
|
||
|
|
3. **Runtime Validation Commands**: Provide the user with these commands to verify the migration worked once they log back in:
|
||
|
|
- Check if Lua is the active config provider:
|
||
|
|
```bash
|
||
|
|
hyprctl systeminfo | grep -i config
|
||
|
|
# Should output: configProvider: lua
|
||
|
|
```
|
||
|
|
- Check if Lua keybindings are registered:
|
||
|
|
```bash
|
||
|
|
hyprctl binds | grep -i "__lua" | head -n 5
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Cleanup Phase**: Once the user confirms the Lua configuration is active and working correctly, instruct the user to remove the old `.conf` files (or do it for them if requested). Leave `hypridle.conf`, `hyprlock.conf`, and `hyprpaper.conf` intact!
|