hypr: replace hypr-workspace-layout shell script with native Lua
Port all layout management to Hyprland v0.55 Lua API: - Per-workspace layout toggle/cycle via hl.workspace_rule + hl.get_active_window() - Layout-aware move/nav/resize as pure Lua functions - Group smart-join via hl.get_active_window().grouped - mfact exact handlers in all custom scroll/swap layouts - No io.popen, no exec_cmd, no IPC deadlock risk
This commit is contained in:
@@ -10,6 +10,9 @@ local filemanager = "uwsm app -- nautilus"
|
||||
local launcher = "uwsm app -- owlry -p app,cmd,system,ssh"
|
||||
local clipman = "uwsm app -- owlry -m clipboard"
|
||||
local browser = "uwsm app -- firefox"
|
||||
local browserprv = "uwsm app -- firefox --private-window"
|
||||
local browsernewinst = "uwsm app -- firefox --new-instance"
|
||||
local altbrowser = "uwsm app -- chromium"
|
||||
local taskman = "uwsm app -- owlry -m uuctl"
|
||||
local pwdmgr = "uwsm app -- bitwarden-desktop"
|
||||
local soundctl = "uwsm app -- pwvucontrol"
|
||||
@@ -24,7 +27,10 @@ hl.bind(mainMod .. " + SHIFT + Return", hl.dsp.exec_cmd(term_tmux))
|
||||
hl.bind(mainMod .. " + CTRL + Return", hl.dsp.exec_cmd(term_tmux_append))
|
||||
hl.bind(mainMod .. " + F1", hl.dsp.exec_cmd("hypr-show-binds"))
|
||||
hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(filemanager))
|
||||
hl.bind(mainMod .. " + W", hl.dsp.exec_cmd(browser))
|
||||
hl.bind(mainMod .. " + W", hl.dsp.exec_cmd(browser))
|
||||
hl.bind(mainMod .. " + SHIFT + W", hl.dsp.exec_cmd(browserprv))
|
||||
hl.bind(mainMod .. " + CTRL + W", hl.dsp.exec_cmd(altbrowser))
|
||||
hl.bind(mainMod .. " + ALT + W", hl.dsp.exec_cmd(browsernewinst))
|
||||
hl.bind(mainMod .. " + Space", hl.dsp.exec_cmd(launcher))
|
||||
|
||||
-- Secondary launchers
|
||||
@@ -59,7 +65,7 @@ hl.bind(mainMod .. " + End", hl.dsp.exec_cmd("hyprlock"))
|
||||
hl.bind(mainMod .. " + SHIFT + End", hl.dsp.exec_cmd("owlry-power-menu"))
|
||||
|
||||
-- Window management
|
||||
hl.bind(mainMod .. " + Q", hl.dsp.window.kill())
|
||||
hl.bind(mainMod .. " + Q", hl.dsp.window.close())
|
||||
hl.bind(mainMod .. " + SHIFT + Q", hl.dsp.window.kill())
|
||||
hl.bind(mainMod .. " + F", hl.dsp.window.float())
|
||||
hl.bind(mainMod .. " + SHIFT + F", hl.dsp.window.fullscreen({ action = "toggle" }))
|
||||
@@ -78,9 +84,140 @@ hl.bind(mainMod .. " + O", hl.dsp.focus({ monitor = "r" }))
|
||||
hl.bind(mainMod .. " + SHIFT + I", hl.dsp.workspace.move({ monitor = "l" }))
|
||||
hl.bind(mainMod .. " + SHIFT + O", hl.dsp.workspace.move({ monitor = "r" }))
|
||||
|
||||
-- ─── Workspace layout state ───────────────────────────────────────────────────
|
||||
local ws_layouts = {}
|
||||
local ws_cycle_order = { "dwindle", "master", "scrolling", "monocle" }
|
||||
|
||||
local function active_ws_id()
|
||||
local w = hl.get_active_window()
|
||||
return w and w.workspace and w.workspace.id
|
||||
end
|
||||
|
||||
local function cur_ws_layout()
|
||||
local id = active_ws_id()
|
||||
return (id and ws_layouts[id]) or "master"
|
||||
end
|
||||
|
||||
local function set_ws_layout(layout)
|
||||
local id = active_ws_id()
|
||||
hl.notification.create({ text = "Layout: " .. layout, timeout = 1800, icon = "info" })
|
||||
if not id then return end
|
||||
ws_layouts[id] = layout
|
||||
hl.workspace_rule({ workspace = tostring(id), layout = layout })
|
||||
end
|
||||
|
||||
local function ws_toggle_ms()
|
||||
set_ws_layout(cur_ws_layout() == "master" and "scrolling" or "master")
|
||||
end
|
||||
|
||||
local function ws_cycle()
|
||||
local cur = cur_ws_layout()
|
||||
local next_layout = ws_cycle_order[1]
|
||||
for i, v in ipairs(ws_cycle_order) do
|
||||
if v == cur then
|
||||
next_layout = ws_cycle_order[(i % #ws_cycle_order) + 1]
|
||||
break
|
||||
end
|
||||
end
|
||||
set_ws_layout(next_layout)
|
||||
end
|
||||
|
||||
local function move_window(dir)
|
||||
return function()
|
||||
local cur = cur_ws_layout()
|
||||
if cur == "scrolling" and (dir == "l" or dir == "r") then
|
||||
hl.dispatch(hl.dsp.layout("swapcol " .. dir))
|
||||
elseif cur ~= "monocle" then
|
||||
hl.dispatch(hl.dsp.window.move({ direction = dir }))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function nav(dir)
|
||||
return function()
|
||||
local cur = cur_ws_layout()
|
||||
if cur == "scrolling" then
|
||||
hl.dispatch(hl.dsp.layout("focus " .. dir))
|
||||
elseif cur == "master" and dir == "d" then
|
||||
hl.dispatch(hl.dsp.layout("addmaster"))
|
||||
elseif cur == "master" and dir == "u" then
|
||||
hl.dispatch(hl.dsp.layout("removemaster"))
|
||||
else
|
||||
hl.dispatch(hl.dsp.focus({ direction = dir }))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function resize_h(sign)
|
||||
return function()
|
||||
local cur = cur_ws_layout()
|
||||
if cur == "scrolling" then
|
||||
hl.dispatch(hl.dsp.layout("colresize " .. sign .. "0.1"))
|
||||
elseif cur ~= "monocle" then
|
||||
hl.dispatch(hl.dsp.window.resize({ x = sign == "+" and 25 or -25, y = 0, relative = true }))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Layout
|
||||
hl.bind(mainMod .. " + comma", hl.dsp.exec_cmd("hypr-workspace-layout toggle-ms"))
|
||||
hl.bind(mainMod .. " + period", hl.dsp.exec_cmd("hypr-workspace-layout cycle"))
|
||||
hl.bind(mainMod .. " + comma", ws_toggle_ms)
|
||||
hl.bind(mainMod .. " + period", ws_cycle)
|
||||
|
||||
-- Split ratio (ALT+1-9 = 10%-90%, ALT+0 = 95%)
|
||||
-- mfact exact for master/custom layouts, colresize for built-in scrolling
|
||||
local mfact_layouts = {
|
||||
["master"] = true,
|
||||
["lua:master-scroll"] = true,
|
||||
["lua:slave-master-scroll"] = true,
|
||||
["lua:center-master-scroll"] = true,
|
||||
["lua:top-master-scroll"] = true,
|
||||
["lua:center-master-scroll-v"] = true,
|
||||
["lua:master-swap"] = true,
|
||||
["lua:slave-master-swap"] = true,
|
||||
["lua:top-master-swap"] = true,
|
||||
}
|
||||
local function layout_ratio(ratio)
|
||||
return function()
|
||||
local cur = hl.get_config("general.layout")
|
||||
if mfact_layouts[cur] then
|
||||
hl.dispatch(hl.dsp.layout("mfact exact " .. ratio))
|
||||
elseif cur == "scrolling" then
|
||||
hl.dispatch(hl.dsp.layout("colresize " .. ratio))
|
||||
end
|
||||
end
|
||||
end
|
||||
local function layout_ratio_delta(delta)
|
||||
return function()
|
||||
local cur = hl.get_config("general.layout")
|
||||
local sign = delta > 0 and "+" or ""
|
||||
if mfact_layouts[cur] then
|
||||
hl.dispatch(hl.dsp.layout("mfact " .. sign .. delta))
|
||||
elseif cur == "scrolling" then
|
||||
hl.dispatch(hl.dsp.layout("colresize " .. sign .. delta))
|
||||
end
|
||||
end
|
||||
end
|
||||
for i = 1, 9 do
|
||||
hl.bind(mainMod .. " + ALT + " .. i, layout_ratio(i / 10))
|
||||
end
|
||||
hl.bind(mainMod .. " + ALT + 0", layout_ratio(0.95))
|
||||
hl.bind(mainMod .. " + ALT + comma", layout_ratio_delta(-0.05))
|
||||
hl.bind(mainMod .. " + ALT + period", layout_ratio_delta(0.05))
|
||||
|
||||
-- Scrolling layout: resize ALL columns
|
||||
for i = 1, 9 do
|
||||
local ratio = i / 10
|
||||
hl.bind(mainMod .. " + CTRL + ALT + " .. i, function()
|
||||
if hl.get_config("general.layout") == "scrolling" then
|
||||
hl.dispatch(hl.dsp.layout("colresize all " .. ratio))
|
||||
end
|
||||
end)
|
||||
end
|
||||
hl.bind(mainMod .. " + CTRL + ALT + 0", function()
|
||||
if hl.get_config("general.layout") == "scrolling" then
|
||||
hl.dispatch(hl.dsp.layout("colresize all 0.95"))
|
||||
end
|
||||
end)
|
||||
|
||||
-- Smart Gaps Toggle
|
||||
hl.bind(mainMod .. " + SHIFT + G", function()
|
||||
@@ -108,20 +245,35 @@ hl.bind(mainMod .. " + L", hl.dsp.focus({ direction = "r" }))
|
||||
hl.bind(mainMod .. " + K", hl.dsp.focus({ direction = "u" }))
|
||||
hl.bind(mainMod .. " + J", hl.dsp.focus({ direction = "d" }))
|
||||
|
||||
-- Move window
|
||||
hl.bind(mainMod .. " + SHIFT + H", hl.dsp.exec_cmd("hypr-workspace-layout move-left"))
|
||||
hl.bind(mainMod .. " + SHIFT + L", hl.dsp.exec_cmd("hypr-workspace-layout move-right"))
|
||||
hl.bind(mainMod .. " + SHIFT + K", hl.dsp.exec_cmd("hypr-workspace-layout move-up"))
|
||||
hl.bind(mainMod .. " + SHIFT + J", hl.dsp.exec_cmd("hypr-workspace-layout move-down"))
|
||||
-- Move window (layout-aware)
|
||||
hl.bind(mainMod .. " + SHIFT + H", move_window("l"))
|
||||
hl.bind(mainMod .. " + SHIFT + L", move_window("r"))
|
||||
hl.bind(mainMod .. " + SHIFT + K", move_window("u"))
|
||||
hl.bind(mainMod .. " + SHIFT + J", move_window("d"))
|
||||
|
||||
-- Resize submap
|
||||
hl.bind(mainMod .. " + R", hl.dsp.submap("resize"))
|
||||
hl.define_submap("resize", function()
|
||||
hl.bind("h", hl.dsp.window.resize({ x = -25, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind("l", hl.dsp.window.resize({ x = 25, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind("k", hl.dsp.window.resize({ x = 0, y = -25, relative = true }), { repeating = true })
|
||||
hl.bind("j", hl.dsp.window.resize({ x = 0, y = 25, relative = true }), { repeating = true })
|
||||
hl.bind("h", hl.dsp.window.resize({ x = -25, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind("l", hl.dsp.window.resize({ x = 25, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind("k", hl.dsp.window.resize({ x = 0, y = -25, relative = true }), { repeating = true })
|
||||
hl.bind("j", hl.dsp.window.resize({ x = 0, y = 25, relative = true }), { repeating = true })
|
||||
hl.bind("SHIFT + h", hl.dsp.window.resize({ x = -60, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind("SHIFT + l", hl.dsp.window.resize({ x = 60, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind("SHIFT + k", hl.dsp.window.resize({ x = 0, y = -60, relative = true }), { repeating = true })
|
||||
hl.bind("SHIFT + j", hl.dsp.window.resize({ x = 0, y = 60, relative = true }), { repeating = true })
|
||||
hl.bind("Escape", hl.dsp.submap("reset"))
|
||||
hl.bind("Return", hl.dsp.submap("reset"))
|
||||
end)
|
||||
|
||||
-- Zoom submap
|
||||
hl.bind(mainMod .. " + M", hl.dsp.submap("zoom"))
|
||||
hl.define_submap("zoom", function()
|
||||
hl.bind("equal", hl.dsp.exec_cmd("hypr-zoom-step +0.2"))
|
||||
hl.bind("minus", hl.dsp.exec_cmd("hypr-zoom-step -0.2"))
|
||||
hl.bind("0", hl.dsp.exec_cmd("hypr-zoom-step reset"))
|
||||
hl.bind("Escape", hl.dsp.submap("reset"))
|
||||
hl.bind("Return", hl.dsp.submap("reset"))
|
||||
end)
|
||||
|
||||
-- Workspace cycling
|
||||
@@ -137,16 +289,10 @@ hl.bind(mainMod .. " + 0", hl.dsp.focus({ workspace = 30 }))
|
||||
hl.bind(mainMod .. " + SHIFT + 0", hl.dsp.window.move({ workspace = 30 }))
|
||||
|
||||
-- Groups
|
||||
local function is_grouped()
|
||||
local h = io.popen("hyprctl activewindow -j 2>/dev/null")
|
||||
if not h then return false end
|
||||
local out = h:read("*a"); h:close()
|
||||
local arr = out:match('"grouped":%[(.-)%]')
|
||||
return arr ~= nil and arr ~= "" and arr ~= "null"
|
||||
end
|
||||
|
||||
local function smart_group(dir)
|
||||
if is_grouped() then
|
||||
local w = hl.get_active_window()
|
||||
local grouped = w and type(w.grouped) == "table" and #w.grouped > 0
|
||||
if grouped then
|
||||
hl.dispatch(hl.dsp.window.move({ out_of_group = true }))
|
||||
else
|
||||
hl.dispatch(hl.dsp.window.move({ into_or_create_group = dir }))
|
||||
@@ -167,14 +313,14 @@ hl.bind(mainMod .. " + Z", hl.dsp.group.next())
|
||||
hl.bind(mainMod .. " + SHIFT + Z", hl.dsp.group.prev())
|
||||
|
||||
-- Layout-aware navigation
|
||||
hl.bind(mainMod .. " + ALT + H", hl.dsp.exec_cmd("hypr-workspace-layout nav-prev"))
|
||||
hl.bind(mainMod .. " + ALT + L", hl.dsp.exec_cmd("hypr-workspace-layout nav-next"))
|
||||
hl.bind(mainMod .. " + ALT + J", hl.dsp.exec_cmd("hypr-workspace-layout nav-down"))
|
||||
hl.bind(mainMod .. " + ALT + K", hl.dsp.exec_cmd("hypr-workspace-layout nav-up"))
|
||||
hl.bind(mainMod .. " + ALT + Tab", hl.dsp.exec_cmd("hypr-workspace-layout nav-next"))
|
||||
hl.bind(mainMod .. " + ALT + SHIFT + Tab", hl.dsp.exec_cmd("hypr-workspace-layout nav-prev"))
|
||||
hl.bind(mainMod .. " + ALT + SHIFT + J", hl.dsp.exec_cmd("hypr-workspace-layout resize-shrink-h"))
|
||||
hl.bind(mainMod .. " + ALT + SHIFT + K", hl.dsp.exec_cmd("hypr-workspace-layout resize-grow-h"))
|
||||
hl.bind(mainMod .. " + ALT + H", nav("l"))
|
||||
hl.bind(mainMod .. " + ALT + L", nav("r"))
|
||||
hl.bind(mainMod .. " + ALT + J", nav("d"))
|
||||
hl.bind(mainMod .. " + ALT + K", nav("u"))
|
||||
hl.bind(mainMod .. " + ALT + Tab", nav("r"))
|
||||
hl.bind(mainMod .. " + ALT + SHIFT + Tab", nav("l"))
|
||||
hl.bind(mainMod .. " + ALT + SHIFT + J", resize_h("-"))
|
||||
hl.bind(mainMod .. " + ALT + SHIFT + K", resize_h("+"))
|
||||
|
||||
-- Mouse binds
|
||||
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
||||
@@ -207,6 +353,13 @@ hl.bind(mainMod .. " + Print", hl.dsp.exec_cmd("owlry-screenshot-menu"))
|
||||
hl.bind("SHIFT + Print", hl.dsp.exec_cmd("uwsm app -- kitty --class=scrrec -e wf-recorder -f ~/Videos/scrrec.mkv -y -g \"$(slurp)\""))
|
||||
|
||||
-- Multimedia
|
||||
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true })
|
||||
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { repeating = true })
|
||||
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true })
|
||||
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true })
|
||||
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { repeating = true })
|
||||
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true })
|
||||
hl.bind("SHIFT + XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%+"), { repeating = true })
|
||||
hl.bind("SHIFT + XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%-"), { repeating = true })
|
||||
hl.bind("SHIFT + XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"), { locked = true })
|
||||
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
|
||||
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
|
||||
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
|
||||
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
|
||||
|
||||
@@ -115,6 +115,9 @@ hl.layout.register("master-scroll", {
|
||||
if msg == "scrolldown" then ms.offset = math.min(ms.offset + 1, max_off); return true
|
||||
elseif msg == "scrollup" then ms.offset = math.max(ms.offset - 1, 0); return true
|
||||
elseif msg == "reset" then ms.offset = 0; return true
|
||||
else
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact = math.max(0.1, math.min(0.95, tonumber(v) or mfact)); return true end
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -144,6 +147,9 @@ hl.layout.register("slave-master-scroll", {
|
||||
if msg == "scrolldown" then sm.offset = math.min(sm.offset + 1, max_off); return true
|
||||
elseif msg == "scrollup" then sm.offset = math.max(sm.offset - 1, 0); return true
|
||||
elseif msg == "reset" then sm.offset = 0; return true
|
||||
else
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact = math.max(0.1, math.min(0.95, tonumber(v) or mfact)); return true end
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -218,6 +224,9 @@ hl.layout.register("center-master-scroll", {
|
||||
if addr and cm_right_addrs[addr] then return scroll_col(cm_right, -1) end
|
||||
elseif msg == "reset" then
|
||||
cm_left.offset = 0; cm_right.offset = 0; return true
|
||||
else
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact = math.max(0.1, math.min(0.95, tonumber(v) or mfact)); return true end
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -312,6 +321,9 @@ hl.layout.register("top-master-scroll", {
|
||||
if msg == "scrolldown" then tm.offset = math.min(tm.offset + 1, max_off); return true
|
||||
elseif msg == "scrollup" then tm.offset = math.max(tm.offset - 1, 0); return true
|
||||
elseif msg == "reset" then tm.offset = 0; return true
|
||||
else
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact_v = math.max(0.1, math.min(0.95, tonumber(v) or mfact_v)); return true end
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -381,6 +393,9 @@ hl.layout.register("center-master-scroll-v", {
|
||||
if addr and cm_vbot_addrs[addr] then return scroll_row(cm_vbot, cm_vbot_addrs, -1) end
|
||||
elseif msg == "reset" then
|
||||
cm_vtop.offset = 0; cm_vbot.offset = 0; return true
|
||||
else
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact_v = math.max(0.1, math.min(0.95, tonumber(v) or mfact_v)); return true end
|
||||
end
|
||||
end,
|
||||
})
|
||||
@@ -422,7 +437,11 @@ hl.layout.register("master-swap", {
|
||||
targets[si]:place({ x=slave_area.x, y=slave_area.y+(j-1)*h, w=slave_area.w, h=h })
|
||||
end
|
||||
end,
|
||||
layout_msg = function(_, msg) if msg == "recalc" then return true end end,
|
||||
layout_msg = function(_, msg)
|
||||
if msg == "recalc" then return true end
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact = math.max(0.1, math.min(0.95, tonumber(v) or mfact)); return true end
|
||||
end,
|
||||
})
|
||||
|
||||
-- ─── slave-master-swap: slaves left, master right ─────────────────────────────
|
||||
@@ -448,7 +467,11 @@ hl.layout.register("slave-master-swap", {
|
||||
targets[si]:place({ x=slave_area.x, y=slave_area.y+(j-1)*h, w=slave_area.w, h=h })
|
||||
end
|
||||
end,
|
||||
layout_msg = function(_, msg) if msg == "recalc" then return true end end,
|
||||
layout_msg = function(_, msg)
|
||||
if msg == "recalc" then return true end
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact = math.max(0.1, math.min(0.95, tonumber(v) or mfact)); return true end
|
||||
end,
|
||||
})
|
||||
|
||||
-- ─── top-master-swap: master top, slaves bottom row ───────────────────────────
|
||||
@@ -471,7 +494,11 @@ hl.layout.register("top-master-swap", {
|
||||
targets[si]:place({ x=slave_area.x+(j-1)*w, y=slave_area.y, w=w, h=slave_area.h })
|
||||
end
|
||||
end,
|
||||
layout_msg = function(_, msg) if msg == "recalc" then return true end end,
|
||||
layout_msg = function(_, msg)
|
||||
if msg == "recalc" then return true end
|
||||
local v = msg:match("^mfact exact (.+)$")
|
||||
if v then mfact_v = math.max(0.1, math.min(0.95, tonumber(v) or mfact_v)); return true end
|
||||
end,
|
||||
})
|
||||
|
||||
-- ─── Auto-scroll on focus + swap-on-focus ────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user