local mainMod = "SUPER" -- Apps local terminal = "uwsm app -- kitty" local term_tmux = "uwsm app -- kitty -e tmux" local term_tmux_append = "uwsm app -- kitty -e tmux a" local editor = "uwsm app -- kitty -e nvim" local alteditor = "uwsm app -- zeditor" local filemanager = "uwsm app -- nautilus" local launcher = "uwsm app -- owlry" local devlauncher = "uwsm app -- owlry --profile dev" 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" local notcenter = "uwsm app -- swaync-client -t -sw" local notdnd = "uwsm app -- swaync-client -d" local nothide = "uwsm app -- swaync-client --hide-latest" -- First-class launchers hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(terminal), { description = "Terminal" }) hl.bind(mainMod .. " + SHIFT + Return", hl.dsp.exec_cmd(term_tmux), { description = "Terminal (tmux)" }) hl.bind(mainMod .. " + CTRL + Return", hl.dsp.exec_cmd(term_tmux_append), { description = "Terminal (attach tmux)" }) hl.bind(mainMod .. " + F1", hl.dsp.exec_cmd("qs ipc call keybinds toggle"), { description = "Show keybinds" }) hl.bind(mainMod .. " + ALT + F1", hl.dsp.exec_cmd("hypr-show-binds"), { description = "Show raw binds (owlry)" }) hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(filemanager), { description = "File manager" }) hl.bind(mainMod .. " + W", hl.dsp.exec_cmd(browser), { description = "Browser" }) hl.bind(mainMod .. " + SHIFT + W", hl.dsp.exec_cmd(browserprv), { description = "Browser (private)" }) hl.bind(mainMod .. " + CTRL + W", hl.dsp.exec_cmd(altbrowser), { description = "Chromium" }) hl.bind(mainMod .. " + ALT + W", hl.dsp.exec_cmd(browsernewinst),{ description = "Firefox (new instance)" }) hl.bind(mainMod .. " + Space", hl.dsp.exec_cmd(launcher), { description = "App launcher" }) hl.bind(mainMod .. " + D", hl.dsp.exec_cmd(devlauncher), { description = "App launcher (dev)" }) -- Secondary launchers hl.bind(mainMod .. " + SHIFT + E", hl.dsp.exec_cmd(editor), { description = "Neovim" }) hl.bind(mainMod .. " + CTRL + E", hl.dsp.exec_cmd(alteditor), { description = "Zed editor" }) hl.bind(mainMod .. " + X", hl.dsp.exec_cmd(taskman), { description = "Task manager" }) hl.bind(mainMod .. " + C", hl.dsp.exec_cmd(clipman), { description = "Clipboard history" }) hl.bind(mainMod .. " + F4", hl.dsp.exec_cmd(soundctl), { description = "Audio mixer" }) -- Quick Workspaces submap hl.bind(mainMod .. " + A", hl.dsp.submap("quickws"), { description = "Quick workspace jump" }) hl.define_submap("quickws", function() hl.bind("z", function() hl.dispatch(hl.dsp.focus({ workspace = 1 })); hl.dispatch(hl.dsp.submap("reset")) end) hl.bind("d", function() hl.dispatch(hl.dsp.focus({ workspace = 2 })); hl.dispatch(hl.dsp.submap("reset")) end) hl.bind("a", function() hl.dispatch(hl.dsp.focus({ workspace = 3 })); hl.dispatch(hl.dsp.submap("reset")) end) hl.bind("x", function() hl.dispatch(hl.dsp.focus({ workspace = 4 })); hl.dispatch(hl.dsp.submap("reset")) end) hl.bind("s", function() hl.dispatch(hl.dsp.focus({ workspace = 5 })); hl.dispatch(hl.dsp.submap("reset")) end) hl.bind("c", function() hl.dispatch(hl.dsp.focus({ workspace = 6 })); hl.dispatch(hl.dsp.submap("reset")) end) hl.bind("Escape", hl.dsp.submap("reset")) hl.bind("Return", hl.dsp.submap("reset")) end) -- Notifications hl.bind(mainMod .. " + grave", hl.dsp.exec_cmd(notcenter), { description = "Notification center" }) hl.bind(mainMod .. " + SHIFT + grave", hl.dsp.exec_cmd(notdnd), { description = "Toggle do-not-disturb" }) hl.bind(mainMod .. " + CTRL + grave", hl.dsp.exec_cmd(nothide), { description = "Hide latest notification" }) -- Session hl.bind(mainMod .. " + Pause", hl.dsp.exec_cmd("hyprlock"), { description = "Lock screen" }) hl.bind(mainMod .. " + SHIFT + Pause", hl.dsp.exec_cmd("owlry-power-menu"), { description = "Power menu" }) hl.bind(mainMod .. " + End", hl.dsp.exec_cmd("hyprlock"), { description = "Lock screen" }) hl.bind(mainMod .. " + SHIFT + End", hl.dsp.exec_cmd("owlry-power-menu"), { description = "Power menu" }) -- Window management hl.bind(mainMod .. " + Q", hl.dsp.window.close(), { description = "Close window" }) hl.bind(mainMod .. " + SHIFT + Q", hl.dsp.window.kill(), { description = "Kill window" }) hl.bind(mainMod .. " + F", hl.dsp.window.float(), { description = "Toggle float" }) hl.bind(mainMod .. " + SHIFT + F", hl.dsp.window.fullscreen({ action = "toggle" }), { description = "Toggle fullscreen" }) hl.bind(mainMod .. " + P", hl.dsp.window.pin(), { description = "Pin window" }) hl.bind(mainMod .. " + U", hl.dsp.focus({ urgent_or_last = true }), { description = "Focus urgent / last" }) hl.bind(mainMod .. " + V", hl.dsp.window.center(), { description = "Center window" }) -- Special workspaces hl.bind(mainMod .. " + SHIFT + Space", hl.dsp.workspace.toggle_special(), { description = "Toggle scratchpad" }) hl.bind(mainMod .. " + CTRL + Space", hl.dsp.window.move({ workspace = "special" }), { description = "Move to scratchpad" }) hl.bind(mainMod .. " + N", hl.dsp.workspace.toggle_special("passwordmgr"), { description = "Password manager scratchpad" }) -- Monitor focus hl.bind(mainMod .. " + I", hl.dsp.focus({ monitor = "l" }), { description = "Focus left monitor" }) hl.bind(mainMod .. " + O", hl.dsp.focus({ monitor = "r" }), { description = "Focus right monitor" }) hl.bind(mainMod .. " + SHIFT + I", hl.dsp.workspace.move({ monitor = "l" }), { description = "Move workspace to left monitor" }) hl.bind(mainMod .. " + SHIFT + O", hl.dsp.workspace.move({ monitor = "r" }), { description = "Move workspace to right monitor" }) -- ─── Workspace layout state ─────────────────────────────────────────────────── -- Shared with layout.lua (the window.active handler reads it for swap layouts). local ws_layouts = require("state").ws_layouts local ws_cycle_order = { "master", "lua:master-scroll", "lua:slave-master-scroll", "lua:center-master-scroll", "lua:top-master-scroll", "lua:center-master-scroll-v", "lua:master-swap", "lua:slave-master-swap", "lua:top-master-swap", "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() local cur = cur_ws_layout() -- Any master variant → scrolling; scrolling/monocle → master local next_layout = (cur == "scrolling" or cur == "monocle") and "master" or "scrolling" set_ws_layout(next_layout) 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", ws_toggle_ms, { description = "Toggle master/scroll layout" }) hl.bind(mainMod .. " + period", ws_cycle, { description = "Cycle workspace layout" }) -- 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 = cur_ws_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 = cur_ws_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), { description = "Split ratio " .. (i * 10) .. "%" }) end hl.bind(mainMod .. " + ALT + 0", layout_ratio(0.95), { description = "Split ratio 95%" }) hl.bind(mainMod .. " + ALT + comma", layout_ratio_delta(-0.05), { description = "Split ratio -5%" }) hl.bind(mainMod .. " + ALT + period", layout_ratio_delta(0.05), { description = "Split ratio +5%" }) -- Scrolling layout: resize ALL columns for i = 1, 9 do local ratio = i / 10 hl.bind(mainMod .. " + CTRL + ALT + " .. i, function() if cur_ws_layout() == "scrolling" then hl.dispatch(hl.dsp.layout("colresize all " .. ratio)) end end, { description = "All columns " .. (i * 10) .. "% (scrolling)" }) end hl.bind(mainMod .. " + CTRL + ALT + 0", function() if cur_ws_layout() == "scrolling" then hl.dispatch(hl.dsp.layout("colresize all 0.95")) end end, { description = "All columns 95% (scrolling)" }) -- Smart Gaps Toggle hl.bind(mainMod .. " + SHIFT + G", function() local gapsIn = hl.get_config("general.gaps_in") local current = type(gapsIn) == "table" and (gapsIn.top or 0) or (gapsIn or 0) if current ~= 0 then hl.config({ general = { gaps_in = 0, gaps_out = 0 } }) hl.notification.create({ text = "Gaps: OFF", timeout = 2000, icon = "info" }) else hl.config({ general = { gaps_in = 5, gaps_out = 5 } }) hl.notification.create({ text = "Gaps: ON", timeout = 2000, icon = "ok" }) end end, { description = "Toggle gaps" }) -- Vim-like navigation hl.bind(mainMod .. " + H", hl.dsp.focus({ direction = "l" }), { description = "Focus left" }) hl.bind(mainMod .. " + L", hl.dsp.focus({ direction = "r" }), { description = "Focus right" }) hl.bind(mainMod .. " + K", hl.dsp.focus({ direction = "u" }), { description = "Focus up" }) hl.bind(mainMod .. " + J", hl.dsp.focus({ direction = "d" }), { description = "Focus down" }) -- Move window (layout-aware) hl.bind(mainMod .. " + SHIFT + H", move_window("l"), { description = "Move window left" }) hl.bind(mainMod .. " + SHIFT + L", move_window("r"), { description = "Move window right" }) hl.bind(mainMod .. " + SHIFT + K", move_window("u"), { description = "Move window up" }) hl.bind(mainMod .. " + SHIFT + J", move_window("d"), { description = "Move window down" }) -- Resize submap hl.bind(mainMod .. " + R", hl.dsp.submap("resize"), { description = "Resize mode" }) 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("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"), { description = "Zoom mode" }) 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 hl.bind(mainMod .. " + Tab", hl.dsp.focus({ workspace = "m+1" }), { description = "Next workspace" }) hl.bind(mainMod .. " + SHIFT + Tab", hl.dsp.focus({ workspace = "m-1" }), { description = "Previous workspace" }) -- Switch Workspaces 1-10 (IDs 21-30 in setup) for i = 1, 9 do hl.bind(mainMod .. " + " .. i, hl.dsp.focus({ workspace = 20 + i }), { description = "Workspace " .. i }) hl.bind(mainMod .. " + SHIFT + " .. i, hl.dsp.window.move({ workspace = 20 + i }), { description = "Move to workspace " .. i }) end hl.bind(mainMod .. " + 0", hl.dsp.focus({ workspace = 30 }), { description = "Workspace 10" }) hl.bind(mainMod .. " + SHIFT + 0", hl.dsp.window.move({ workspace = 30 }), { description = "Move to workspace 10" }) -- Groups local function smart_group(dir) 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 })) end end hl.bind(mainMod .. " + G", hl.dsp.group.toggle(), { description = "Toggle group" }) hl.bind(mainMod .. " + CTRL + G", hl.dsp.window.move({ out_of_group = true }), { description = "Leave group" }) hl.bind(mainMod .. " + CTRL + H", hl.dsp.group.prev(), { description = "Group: previous tab" }) hl.bind(mainMod .. " + CTRL + L", hl.dsp.group.next(), { description = "Group: next tab" }) hl.bind(mainMod .. " + CTRL + J", hl.dsp.group.move_window({ forward = true }), { description = "Group: move tab forward" }) hl.bind(mainMod .. " + CTRL + K", hl.dsp.group.move_window({ forward = false }), { description = "Group: move tab back" }) hl.bind(mainMod .. " + CTRL + SHIFT + H", function() smart_group("l") end, { description = "Smart group left" }) hl.bind(mainMod .. " + CTRL + SHIFT + J", function() smart_group("d") end, { description = "Smart group down" }) hl.bind(mainMod .. " + CTRL + SHIFT + K", function() smart_group("u") end, { description = "Smart group up" }) hl.bind(mainMod .. " + CTRL + SHIFT + L", function() smart_group("r") end, { description = "Smart group right" }) hl.bind(mainMod .. " + Z", hl.dsp.group.next(), { description = "Group: next tab" }) hl.bind(mainMod .. " + SHIFT + Z", hl.dsp.group.prev(), { description = "Group: previous tab" }) -- Layout-aware navigation hl.bind(mainMod .. " + ALT + H", nav("l"), { description = "Nav left (layout-aware)" }) hl.bind(mainMod .. " + ALT + L", nav("r"), { description = "Nav right (layout-aware)" }) hl.bind(mainMod .. " + ALT + J", nav("d"), { description = "Nav down / add master" }) hl.bind(mainMod .. " + ALT + K", nav("u"), { description = "Nav up / remove master" }) hl.bind(mainMod .. " + ALT + Tab", nav("r"), { description = "Nav right (layout-aware)" }) hl.bind(mainMod .. " + ALT + SHIFT + Tab", nav("l"), { description = "Nav left (layout-aware)" }) hl.bind(mainMod .. " + ALT + SHIFT + J", resize_h("-"), { description = "Shrink split" }) hl.bind(mainMod .. " + ALT + SHIFT + K", resize_h("+"), { description = "Grow split" }) -- Mouse binds hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) -- master-scroll slave scrolling (mouse wheel + bracket fallback) -- Guard by layout name: built-in and swap layouts error on unknown scroll messages. local scroll_layouts = { ["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, } local function layout_scroll(dir) return function() if scroll_layouts[cur_ws_layout()] then hl.dispatch(hl.dsp.layout(dir)) end end end hl.bind(mainMod .. " + mouse_down", layout_scroll("scrollup")) hl.bind(mainMod .. " + mouse_up", layout_scroll("scrolldown")) hl.bind(mainMod .. " + bracketright", layout_scroll("scrolldown"), { description = "Scroll slave pane down" }) hl.bind(mainMod .. " + bracketleft", layout_scroll("scrollup"), { description = "Scroll slave pane up" }) -- Screenshots hl.bind("Print", hl.dsp.exec_cmd("grimblast --notify copy screen"), { description = "Screenshot: full screen" }) hl.bind(mainMod .. " + Print", hl.dsp.exec_cmd("owlry-screenshot-menu"), { description = "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)\""), { description = "Screen record selection" }) -- Multimedia hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"), { repeating = true, description = "Volume up" }) hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), { repeating = true, description = "Volume down" }) hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"), { locked = true, description = "Mute" }) hl.bind("SHIFT + XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%+"), { repeating = true, description = "Mic volume up" }) hl.bind("SHIFT + XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 5%-"), { repeating = true, description = "Mic volume down" }) hl.bind("SHIFT + XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"),{ locked = true, description = "Mic mute" }) hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true, description = "Play/pause" }) hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true, description = "Play/pause" }) hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true, description = "Next track" }) hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true, description = "Previous track" }) -- Brightness — let brightnessctl do the math, then push the new percentage to -- the Quickshell OSD (field 4 of `-m` output is the percentage, e.g. "70%"). local function brightness_step(arg) -- Single brightnessctl call: `-m s` sets and prints the machine line -- (device,class,current,percent,max); field 4 is the new percentage. return hl.dsp.exec_cmd( "qs ipc call osd brightness \"$(brightnessctl -e4 -n2 -m s " .. arg .. " | cut -d, -f4 | tr -d %)\"" ) end hl.bind("XF86MonBrightnessUp", brightness_step("1%+"), { locked = true, repeating = true, description = "Brightness up (1%)" }) hl.bind("XF86MonBrightnessDown", brightness_step("1%-"), { locked = true, repeating = true, description = "Brightness down (1%)" }) hl.bind("SHIFT + XF86MonBrightnessUp", brightness_step("5%+"), { locked = true, repeating = true, description = "Brightness up (5%)" }) hl.bind("SHIFT + XF86MonBrightnessDown", brightness_step("5%-"), { locked = true, repeating = true, description = "Brightness down (5%)" }) hl.bind("CTRL + XF86MonBrightnessUp", brightness_step("70%"), { locked = true, description = "Brightness 70%" }) hl.bind("CTRL + XF86MonBrightnessDown", brightness_step("20%"), { locked = true, description = "Brightness 20%" }) -- Blue light filter manual override -- TODO: Temporary workaround until `hyprctl hyprsunset reset` is supported in local packages. -- Once supported, replace `get_scheduled_temperature` and `systemctl`/`hyprctl` workaround with `hyprctl hyprsunset reset`. local function get_scheduled_temperature() local time = os.date("*t") local mins = time.hour * 60 + time.min -- Profiles matching ~/.config/hypr/hyprsunset.conf: -- Day: 07:00 (420 mins) -> identity -- Evening: 19:30 (1170 mins) -> 4500 -- Night: 22:30 (1350 mins) -> 3200 if mins >= 1350 or mins < 420 then return 3200 elseif mins >= 1170 and mins < 1350 then return 4500 else return "identity" end end local bluelight_enabled = true local function toggle_bluelight() bluelight_enabled = not bluelight_enabled if bluelight_enabled then local target = get_scheduled_temperature() if target == "identity" then hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprsunset identity")) hl.notification.create({ text = "Blue Light Filter: Auto (OFF/Day)", timeout = 1500, icon = "info" }) else hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprsunset temperature " .. tostring(target))) hl.notification.create({ text = "Blue Light Filter: Auto (" .. tostring(target) .. "K)", timeout = 1500, icon = "ok" }) end else hl.dispatch(hl.dsp.exec_cmd("hyprctl hyprsunset identity")) hl.notification.create({ text = "Blue Light Filter: OFF", timeout = 1500, icon = "info" }) end end hl.bind(mainMod .. " + SHIFT + B", toggle_bluelight, { description = "Toggle blue light filter" }) {{- if (index .chezmoi.config.data.tags "laptop") }} -- ─── Surface Type Cover ────────────────────────────────────────────────────── local tc_touchpad = "microsoft-surface-type-cover-touchpad" local tc_keyboard = "microsoft-surface-type-cover-keyboard" local function device_enabled(name) -- Reads live state; falls back to true on any error so toggles still work. local ok, v = pcall(hl.get_config, "device[" .. name .. "]:enabled") if not ok then return true end if v == nil then return true end return v ~= false and v ~= 0 and v ~= "false" end local function set_device(name, enabled) hl.device({ name = name, enabled = enabled }) end -- SUPER+T: toggle touchpad based on live state (no local-cache desync) hl.bind(mainMod .. " + T", function() local new_state = not device_enabled(tc_touchpad) set_device(tc_touchpad, new_state) hl.notification.create({ text = "Touchpad: " .. (new_state and "ON" or "OFF"), timeout = 1500, icon = new_state and "ok" or "info", }) end, { description = "Toggle touchpad" }) -- Tablet-mode switch: fold cover back → disable kb+touchpad, unfold → re-enable hl.bind("switch:on:Microsoft Surface Type Cover Tablet Mode Switch", function() set_device(tc_touchpad, false) set_device(tc_keyboard, false) hl.notification.create({ text = "Tablet mode", timeout = 1500, icon = "info" }) end, { description = "Tablet mode: disable Type Cover" }) hl.bind("switch:off:Microsoft Surface Type Cover Tablet Mode Switch", function() set_device(tc_touchpad, true) set_device(tc_keyboard, true) hl.notification.create({ text = "Laptop mode", timeout = 1500, icon = "ok" }) end, { description = "Laptop mode: enable Type Cover" }) {{- end }}