Compare commits

...
7 Commits
Author SHA1 Message Date
felis d82e219ec9 persist workspaces 2026-08-23 17:35:24 -07:00
felis 45976f7b8b add scrolling overview 2026-08-23 17:33:21 -07:00
felis 09d35cfc4d remove hyprspace 2026-08-23 16:57:44 -07:00
felis bdec7e77bf use hyprspace for layouts 2026-08-17 00:03:23 -07:00
felis 816eb22b2f add hyprstage binds 2026-08-14 00:11:09 -07:00
felis 29f19bb45d add descriptions to keybinds 2026-08-12 23:27:12 -07:00
felis bfe5419bb9 refactor code structure 2026-08-12 23:15:12 -07:00
19 changed files with 870 additions and 563 deletions
-30
View File
@@ -1,30 +0,0 @@
-------------------
---- AUTOSTART ----
-------------------
-- See https://wiki.hypr.land/Configuring/Basics/Autostart/
-- Autostart necessary processes (like notifications daemons, status bars, etc.)
-- Or execute your favorite apps at launch like this:
--
local function uwsm(cmd)
return "uwsm-app -- " .. cmd
end
-- Set programs that you use
local terminal = uwsm("alacritty")
hl.on("hyprland.start", function()
hl.exec_cmd("fcitx5 -d")
hl.exec_cmd("hyprpm reload -n")
hl.exec_cmd(uwsm(terminal))
hl.exec_cmd(uwsm("hyprpaper"))
hl.exec_cmd(uwsm("firefox"))
hl.exec_cmd(uwsm("wl-paste --type text --watch cliphist store"))
hl.exec_cmd(uwsm("wl-paste --type image --watch cliphist store"))
hl.exec_cmd(uwsm("noctalia"))
end)
+90
View File
@@ -0,0 +1,90 @@
-- Base visual behavior. Theme colors are intentionally applied separately at
-- the end of this module because Noctalia generates them.
hl.config({
-- Window spacing, borders, and compositor-wide rendering behavior.
general = {
gaps_in = 5,
gaps_out = 20,
border_size = 2,
resize_on_border = false,
allow_tearing = false,
},
-- Window shape, opacity, shadows, and background blur.
decoration = {
rounding = 10,
rounding_power = 2,
active_opacity = 1.0,
inactive_opacity = 1.0,
shadow = {
enabled = true,
range = 4,
render_power = 3,
color = 0xee1a1a1a,
},
blur = {
enabled = true,
size = 3,
passes = 1,
vibrancy = 0.1696,
},
},
-- This is the global animation switch; individual animation tracks below
-- control timing and style.
animations = {
enabled = true,
},
-- Keep the built-in wallpaper enabled as a fallback.
misc = {
force_default_wallpaper = -1,
disable_hyprland_logo = false,
},
})
-- Curves are data-driven so adding or tuning one does not require another
-- standalone `hl.curve` call.
local curves = {
{ "easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } } },
{ "easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } } },
{ "linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } } },
{ "almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } } },
{ "quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } } },
{ "easy", { type = "spring", mass = 1, stiffness = 238.1191, dampening = 24.21279333 } },
}
for _, curve in ipairs(curves) do
hl.curve(curve[1], curve[2])
end
-- Animation tracks inherit from broader leaves unless a more specific track
-- (for example windowsIn or workspacesOut) overrides them.
local animations = {
{ leaf = "global", enabled = true, speed = 10, bezier = "default" },
{ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" },
{ leaf = "windows", enabled = true, speed = 4.79, spring = "easy" },
{ leaf = "windowsIn", enabled = true, speed = 4.1, spring = "easy", style = "popin 87%" },
{ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" },
{ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" },
{ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" },
{ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" },
{ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" },
{ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" },
{ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" },
{ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" },
{ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" },
{ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" },
{ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" },
{ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" },
{ leaf = "zoomFactor", enabled = true, speed = 7, bezier = "quick" },
}
for _, animation in ipairs(animations) do
hl.animation(animation)
end
-- Noctalia generates the palette module; appearance owns when it is applied.
require("noctalia").apply_theme()
+29
View File
@@ -0,0 +1,29 @@
-- Build the startup list once, using the same program names and UWSM wrapper as
-- the keybinding module.
local commands = require("lib.commands")
local settings = require("config.settings")
local startup_commands = {
-- Local native layouts must register before a workspace can select them.
"hyprctl plugin load /home/felis/source/hyprstage/build/hyprstage.so",
-- Session services that manage their own lifecycle.
"fcitx5 -d",
"hyprpm reload -n",
-- Graphical/session applications launched inside UWSM scopes.
commands.uwsm(settings.programs.terminal),
commands.uwsm("firefox"),
commands.uwsm("wl-paste --type text --watch cliphist store"),
commands.uwsm("wl-paste --type image --watch cliphist store"),
commands.uwsm("noctalia"),
commands.uwsm("linux-wallpaper-engine")
}
-- This event only fires when Hyprland starts; reloading the config will not
-- launch duplicate copies of these applications.
hl.on("hyprland.start", function()
for _, command in ipairs(startup_commands) do
hl.exec_cmd(command)
end
end)
+6
View File
@@ -0,0 +1,6 @@
-- Keep XCursor and Hyprcursor at the same size for consistent pointers across
-- XWayland and native Wayland applications.
local cursor_size = "24"
hl.env("XCURSOR_SIZE", cursor_size)
hl.env("HYPRCURSOR_SIZE", cursor_size)
+31
View File
@@ -0,0 +1,31 @@
-- Keyboard, pointer, and touchpad defaults shared by all input devices.
hl.config({
input = {
kb_layout = "us",
kb_variant = "",
kb_model = "",
-- Turn Caps Lock into MOD3, which settings.lua exposes as Hyper.
kb_options = "caps:hyper",
kb_rules = "",
follow_mouse = 1,
sensitivity = 0,
touchpad = {
natural_scroll = false,
},
},
})
-- Three-finger horizontal swipes move between workspaces.
hl.gesture({
fingers = 3,
direction = "horizontal",
action = "workspace",
})
-- Example device-specific override retained from the original configuration.
-- It only applies when Hyprland sees a device with this exact name.
hl.device({
name = "epic-mouse-v1",
sensitivity = -0.5,
})
+329
View File
@@ -0,0 +1,329 @@
-- All keybindings live in this module so conflicts can be audited in one
-- place. Shared values come from settings; command wrapping comes from lib.
local commands = require("lib.commands")
local settings = require("config.settings")
local main = settings.modifiers.main
local hyper = settings.modifiers.hyper
-- Build normalized bind strings and avoid hand-written separator mistakes such
-- as doubled `+` characters.
local function chord(...)
return table.concat({ ... }, " + ")
end
-- Stage uses vertical navigation to move through its preview ordering. Keep
-- the ordinary directional-focus behavior for every other tiled layout.
local function focus_or_stage(direction, stage_message)
local focus = hl.dsp.focus({ direction = direction })
if not stage_message then
return focus
end
return function()
local workspace = hl.get_active_special_workspace() or hl.get_active_workspace()
if workspace and workspace.tiled_layout == "stage" then
hl.dispatch(hl.dsp.layout(stage_message))
return
end
hl.dispatch(focus)
end
end
local tiled_layouts = { "dwindle", "master", "scrolling", "monocle", "stage" }
local active_workspace_layout_rules = {}
local function cycle_active_workspace_layout()
local workspace = hl.get_active_special_workspace() or hl.get_active_workspace()
if not workspace or workspace.special then
return
end
local next_index = 1
for index, layout in ipairs(tiled_layouts) do
if layout == workspace.tiled_layout then
next_index = index % #tiled_layouts + 1
break
end
end
local selector = workspace.config_name
if not selector or selector == "" then
selector = tostring(workspace.id)
end
local previous_rule = active_workspace_layout_rules[workspace.id]
if previous_rule then
previous_rule:set_enabled(false)
end
active_workspace_layout_rules[workspace.id] = hl.workspace_rule({
workspace = selector,
layout = tiled_layouts[next_index],
})
end
-- Generate matching focus/move bindings for numbered workspaces. Workspace 10
-- maps to the 0 key because `% 10` returns zero.
local function bind_numbered_workspaces(modifier, move_modifier, count)
for workspace = 1, count do
local key = workspace % 10
hl.bind(
chord(modifier, key),
hl.dsp.focus({ workspace = workspace }),
{ description = "Switch to workspace " .. workspace }
)
hl.bind(
chord(modifier, move_modifier, key),
hl.dsp.window.move({ workspace = workspace }),
{ description = "Send focused window to workspace " .. workspace }
)
end
end
-- Core application and window-management shortcuts.
hl.bind(
chord(main, "SPACE"),
hl.dsp.exec_cmd("noctalia msg panel-toggle launcher"),
{ description = "Toggle Noctalia launcher" }
)
hl.bind(
chord(main, "W"),
hl.dsp.window.close(),
{ description = "Close focused window" }
)
hl.bind(
chord(main, "Q"),
hl.dsp.exec_cmd(commands.uwsm(settings.programs.terminal)),
{ description = "Open terminal" }
)
hl.bind(
chord(main, "E"),
hl.dsp.exec_cmd(commands.uwsm(settings.programs.file_manager)),
{ description = "Open file manager" }
)
hl.bind(
chord(main, "V"),
hl.dsp.window.float({ action = "toggle" }),
{ description = "Toggle focused window floating" }
)
hl.bind(
chord(main, "R"),
hl.dsp.exec_cmd(settings.programs.launcher),
{ description = "Open application launcher" }
)
hl.bind(
chord(main, "P"),
hl.dsp.window.pseudo(),
{ description = "Toggle pseudo-tiling for focused window" }
)
hl.bind(
chord(main, "J"),
hl.dsp.layout("togglesplit"),
{ description = "Toggle dwindle split direction" }
)
-- One direction table drives arrow-key focus, Hyper+WASD focus/movement, and
-- moving the active workspace between adjacent monitors.
local directions = {
{ arrow = "left", key = "A", direction = "l", monitor = "left" },
{ arrow = "right", key = "D", direction = "r", monitor = "right" },
{ arrow = "up", key = "W", direction = "u", monitor = "above", stage_message = "cycleprev" },
{ arrow = "down", key = "S", direction = "d", monitor = "below", stage_message = "cyclenext" },
}
for _, direction in ipairs(directions) do
local description = "Focus window " .. direction.arrow
if direction.stage_message then
description = description .. " / navigate Stage"
end
hl.bind(
chord(main, direction.arrow),
hl.dsp.focus({ direction = direction.arrow }),
{ description = "Focus window " .. direction.arrow }
)
hl.bind(
chord(hyper, direction.key),
focus_or_stage(direction.direction, direction.stage_message),
{ description = description }
)
hl.bind(
chord(hyper, "CTRL", direction.key),
hl.dsp.window.move({ direction = direction.direction }),
{ description = "Move focused window " .. direction.arrow }
)
hl.bind(
chord(hyper, main, direction.key),
hl.dsp.workspace.move({ monitor = direction.direction }),
{ description = "Move workspace to monitor " .. direction.monitor }
)
end
-- Cycle only the active workspace through the configured tiled layouts.
hl.bind(
chord(hyper, "grave"),
cycle_active_workspace_layout,
{ description = "Cycle active workspace layout" }
)
bind_numbered_workspaces(main, "SHIFT", 10)
bind_numbered_workspaces(hyper, "CTRL", 9)
-- Cycle workspaces with the wheel and manipulate windows with mouse buttons.
hl.bind(
chord(main, "mouse_down"),
hl.dsp.focus({ workspace = "e+1" }),
{ description = "Switch to next workspace" }
)
hl.bind(
chord(main, "mouse_up"),
hl.dsp.focus({ workspace = "e-1" }),
{ description = "Switch to previous workspace" }
)
hl.bind(
chord(main, "mouse:272"),
hl.dsp.window.drag(),
{ mouse = true, description = "Drag focused window" }
)
hl.bind(
chord(main, "mouse:273"),
hl.dsp.window.resize(),
{ mouse = true, description = "Resize focused window" }
)
-- Locked media keys also work while the session is locked. Volume and
-- brightness binds repeat while held; playback controls fire once.
local media_binds = {
{
key = "XF86AudioRaiseVolume",
command = "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+",
description = "Increase output volume",
repeating = true,
},
{
key = "XF86AudioLowerVolume",
command = "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-",
description = "Decrease output volume",
repeating = true,
},
{
key = "XF86AudioMute",
command = "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
description = "Toggle output mute",
repeating = true,
},
{
key = "XF86AudioMicMute",
command = "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle",
description = "Toggle microphone mute",
repeating = true,
},
{
key = "XF86MonBrightnessUp",
command = "brightnessctl -e4 -n2 set 5%+",
description = "Increase display brightness",
repeating = true,
},
{
key = "XF86MonBrightnessDown",
command = "brightnessctl -e4 -n2 set 5%-",
description = "Decrease display brightness",
repeating = true,
},
{
key = "XF86AudioNext",
command = "playerctl next",
description = "Play next media track",
},
{
key = "XF86AudioPause",
command = "playerctl play-pause",
description = "Toggle media playback",
},
{
key = "XF86AudioPlay",
command = "playerctl play-pause",
description = "Toggle media playback",
},
{
key = "XF86AudioPrev",
command = "playerctl previous",
description = "Play previous media track",
},
}
for _, bind in ipairs(media_binds) do
hl.bind(bind.key, hl.dsp.exec_cmd(bind.command), {
locked = true,
repeating = bind.repeating,
description = bind.description,
})
end
-- Generate focus/move shortcuts for every named workspace in settings.lua.
for _, workspace in ipairs(settings.named_workspaces) do
hl.bind(
chord(hyper, workspace.key),
hl.dsp.focus({ workspace = workspace.selector }),
{ description = "Switch to " .. workspace.name .. " workspace" }
)
hl.bind(
chord(hyper, "CTRL", workspace.key),
hl.dsp.window.move({ workspace = workspace.selector }),
{ description = "Send focused window to " .. workspace.name .. " workspace" }
)
end
-- Promote the focused client when a workspace is using master layout.
hl.bind(
chord(hyper, "M"),
hl.dsp.layout("swapwithmaster master ignoremaster"),
{ description = "Promote to master" }
)
-- Toggle ScrollOverview with SUPER+g
hl.bind(
chord(hyper, "SPACE"), function()
hl.plugin.scrolloverview.overview("toggle all")
end)
-- Toggle a focused client into an 80%-sized centered floating mode. Returning
-- it to tiled mode lets the active layout restore its previous geometry.
hl.bind(chord(hyper, "F"), function()
local window = hl.get_active_window()
local monitor = hl.get_active_monitor()
if not window or not monitor then
return
end
local was_tiled = not window.floating
hl.dispatch(hl.dsp.window.float({ action = "toggle" }))
if not was_tiled then
return
end
hl.dispatch(hl.dsp.window.resize({
x = math.floor(monitor.width * 0.80),
y = math.floor(monitor.height * 0.80),
relative = false,
}))
hl.dispatch(hl.dsp.window.center())
hl.dispatch(hl.dsp.window.alter_zorder({ mode = "top" }))
end, {
description = "Toggle focused floating window",
})
-- Open the HyprCapture plugin's interactive region/window picker.
hl.bind(
chord(main, "SHIFT", "S"),
hl.plugin.hyprcapture.open,
{ description = "HyprCapture window capture" }
)
+43
View File
@@ -0,0 +1,43 @@
-- Keep layout defaults together so a later module cannot silently override a
-- single field from another `hl.config` call.
hl.config({
-- Dwindle is the default layout for ordinary workspaces.
general = {
layout = "dwindle",
},
-- Remember the user's most recent split direction in dwindle.
dwindle = {
preserve_split = true,
},
-- Named workspaces opt into master layout. New windows enter the slave
-- column, leaving the current master in place.
master = {
mfact = 0.80,
orientation = "left",
new_status = "slave",
},
-- Expand the only column when using Hyprland's scrolling layout.
scrolling = {
fullscreen_on_one_column = true,
},
-- HyprStage is currently used by the right-monitor bilibili workspace, so
-- keep its tucked preview stack on that monitor's right edge.
plugin = {
hyprstage = {
sidebar_side = "right",
},
overview = {
panelColor = "#1d4580",
panelBorderColor = "#05494d",
workspaceActiveBackground = "rgba(49, 50, 68, 0.8)",
workspaceActiveBorder = "rgb(203, 166, 247)",
onBottom = false,
autoDrag = true,
showSpecialWorkspace = true,
}
},
})
+7
View File
@@ -0,0 +1,7 @@
-- Apply the physical display topology defined in the shared settings module.
local settings = require("config.settings")
-- HDMI is the left/high-refresh display; DisplayPort is positioned to its
-- right and also hosts the dedicated named workspaces.
hl.monitor(settings.monitors.primary)
hl.monitor(settings.monitors.secondary)
+110
View File
@@ -0,0 +1,110 @@
local lastWorkspaceScrollBind
local function table_value(value, ...)
if value == nil then
return nil
end
for _, key in ipairs({ ... }) do
local ok, item = pcall(function()
return value[key]
end)
if ok and item ~= nil then
return item
end
end
return nil
end
local function last_workspace_state()
local monitor = hl.get_monitor_at_cursor() or hl.get_active_monitor()
if not monitor then
return false, nil
end
local activeWorkspace = table_value(monitor, "active_workspace")
local activeWorkspaceId = table_value(activeWorkspace, "id")
if type(activeWorkspaceId) ~= "number" or activeWorkspaceId <= 0 then
return false, nil
end
local activeWorkspaceWindows = table_value(activeWorkspace, "windows")
if type(activeWorkspaceWindows) ~= "number" or activeWorkspaceWindows == 0 then
return false, nil
end
local lastWorkspaceId
for _, workspace in ipairs(hl.get_workspaces()) do
local id = table_value(workspace, "id")
if type(id) == "number"
and id > 0
and table_value(workspace, "special") ~= true
and table_value(workspace, "monitor") == monitor then
lastWorkspaceId = math.max(lastWorkspaceId or id, id)
end
end
return activeWorkspaceId == lastWorkspaceId, monitor
end
local function create_workspace_at_end()
local isLastWorkspace, monitor = last_workspace_state()
if not isLastWorkspace or not monitor then
return
end
hl.dispatch(hl.dsp.focus({ monitor = table_value(monitor, "name") }))
hl.dispatch(hl.dsp.focus({ workspace = "emptynm" }))
end
local function update_last_workspace_scroll_bind()
if not lastWorkspaceScrollBind then
return
end
local enabled = false
if hl.get_current_submap() == "scrolloverview" then
enabled = last_workspace_state()
end
lastWorkspaceScrollBind:set_enabled(enabled)
end
hl.define_submap("scrolloverview", function()
hl.bind("left", hl.plugin.scrolloverview.navigate("left"))
hl.bind("right", hl.plugin.scrolloverview.navigate("right"))
hl.bind("up", hl.plugin.scrolloverview.navigate("up"))
hl.bind("down", hl.plugin.scrolloverview.navigate("down"))
hl.bind("return", hl.plugin.scrolloverview.overview("select"))
hl.bind("escape", hl.plugin.scrolloverview.overview("off"))
hl.bind("mouse:272", function()
-- Select the clicked window, or just the workspace if no window was clicked, then close the overview. This is the default behaviour if submap is not defined.
hl.plugin.scrolloverview.overview("select")
hl.plugin.scrolloverview.window("select")
hl.plugin.scrolloverview.overview("off")
end, { mouse = true })
hl.bind("mouse:274", hl.plugin.scrolloverview.window("close"), { mouse = true })
lastWorkspaceScrollBind = hl.bind("mouse_down", create_workspace_at_end)
lastWorkspaceScrollBind:set_enabled(false)
end)
hl.on("workspace.active", update_last_workspace_scroll_bind)
hl.on("workspace.created", update_last_workspace_scroll_bind)
hl.on("workspace.removed", update_last_workspace_scroll_bind)
hl.on("workspace.move_to_monitor", update_last_workspace_scroll_bind)
hl.on("window.open", update_last_workspace_scroll_bind)
hl.on("window.close", update_last_workspace_scroll_bind)
hl.on("window.move_to_workspace", update_last_workspace_scroll_bind)
hl.on("monitor.focused", update_last_workspace_scroll_bind)
hl.on("keybinds.submap", update_last_workspace_scroll_bind)
local lastWorkspaceScrollTimer = hl.timer(update_last_workspace_scroll_bind, {
timeout = 50,
type = "repeat",
})
update_last_workspace_scroll_bind()
+68
View File
@@ -0,0 +1,68 @@
-- Central user-editable values. Domain modules consume this table so monitor
-- names, applications, modifiers, and workspace selectors stay in sync.
return {
-- SUPER is used for conventional desktop shortcuts. Caps Lock is mapped to
-- MOD3 in input.lua and acts as the Hyper modifier.
modifiers = {
main = "SUPER",
hyper = "MOD3",
},
-- Store raw executable names here. Callers decide whether a command should
-- be wrapped with `uwsm-app` or executed directly.
programs = {
terminal = "kitty",
file_manager = "dolphin",
launcher = "hyprlauncher",
},
-- Named outputs make cross-module references (such as workspace routing)
-- clearer than array indexes.
monitors = {
primary = {
output = "HDMI-A-1",
mode = "2560x1440@144",
position = "0x0",
scale = 1,
},
secondary = {
output = "DP-3",
mode = "2560x1440@120",
position = "2560x0",
scale = 1,
},
},
-- Both workspace rules and keybindings are generated from this list.
named_workspaces = {
{
name = "bilibili",
selector = "name:bilibili",
key = "F1",
layout = "stage"
},
{
name = "im",
selector = "name:im",
key = "F2",
layout = "scrolling"
},
},
-- .config/hypr/hyprland.lua
plugin = {
scrolloverview = {
gesture_distance = 300, -- how far is the "max" for the gesture
scale = 0.5, -- preferred overview scale
workspace_gap = 100,
layout = "vertical", -- vertical or horizontal
wallpaper = 2, -- 0: global only, 1: per-workspace only, 2: both
blur = true, -- blur only the main overview wallpaper
shadow = {
enabled = true,
range = 50,
},
},
}
}
+58
View File
@@ -0,0 +1,58 @@
-- Window rules are kept as data and registered in order at the bottom. This
-- makes adding a rule independent from the registration mechanism.
local rules = {
-- Render terminal image previews as undecorated, non-focusing overlays.
{
name = "ueberzugpp-preview",
match = {
class = "ueberzugpp_.*",
},
float = true,
no_initial_focus = true,
border_size = 0,
rounding = 0,
no_shadow = true,
},
-- Ignore application requests to maximize themselves.
{
name = "suppress-maximize-events",
match = {
class = ".*",
},
suppress_event = "maximize",
},
-- Prevent empty XWayland drag helper windows from stealing focus.
{
name = "fix-xwayland-drags",
match = {
class = "^$",
title = "^$",
xwayland = true,
float = true,
fullscreen = false,
pin = false,
},
no_focus = true,
},
-- Place the Hyprland runner near the lower-left edge as a floating window.
{
name = "move-hyprland-run",
match = {
class = "hyprland-run",
},
move = "20 monitor_h-120",
float = true,
},
{
name = "confine-dota2-cursor",
match = {
class = "dota2",
},
confine_pointer = true,
},
}
-- Register every declarative rule with Hyprland.
for _, rule in ipairs(rules) do
hl.window_rule(rule)
end
+35
View File
@@ -0,0 +1,35 @@
-- Create persistent numbered workspaces and dedicated named workspaces.
local settings = require("config.settings")
-- Number keys address workspaces 1-9 directly and the 0 key addresses
-- workspace 10. Odd-numbered workspaces live on the left/primary monitor;
-- even-numbered workspaces, including workspace 10, live on the right/secondary
-- monitor.
for workspace = 1, 10 do
local monitor = settings.monitors.primary.output
if workspace % 2 == 0 then
monitor = settings.monitors.secondary.output
end
hl.workspace_rule({
workspace = tostring(workspace),
monitor = monitor,
persistent = true,
})
end
-- Named workspaces keep their individually selected layouts and remain on the
-- right-hand monitor.
for _, workspace in ipairs(settings.named_workspaces) do
hl.workspace_rule({
workspace = workspace.selector,
layout = workspace.layout or "master",
layout_opts = {
orientation = "left",
},
persistent = true,
-- Keep communication/media workspaces on the right-hand monitor.
monitor = settings.monitors.secondary.output,
})
end
+23 -367
View File
@@ -1,375 +1,31 @@
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Configuration entry point. Each module owns one Hyprland domain and applies
-- AUTOGENERATED HYPRLAND CONFIG. -- -- its configuration when required.
-- EDIT THIS CONFIG ACCORDING TO THE WIKI INSTRUCTIONS. -- local modules = {
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Hardware and environment are loaded first so later modules can rely on
-- the configured outputs and session variables.
"config.monitors",
"config.environment",
-- This is an example Hyprland Lua config file. -- Core compositor behavior and presentation.
-- Refer to the wiki for more information. "config.appearance",
-- https://wiki.hypr.land/Configuring/Start/ "config.layouts",
"config.input",
-- Please note not all available settings / options are set here. -- User-facing rules, shortcuts, and startup integrations.
-- For a full list, see the wiki "config.workspaces",
"config.window_rules",
"config.keybinds",
"config.autostart",
-- You can (and should!!) split this configuration into multiple files -- Scrolling Overview Config
-- Create your files separately and then require them like this: "config.scrolling_overview",
-- require("myColors") }
require("noctalia") -- Requiring a module applies that domain's configuration through the global
require("keybinds") -- Hyprland Lua API (`hl`).
require("workspaces") for _, module in ipairs(modules) do
require("windowrules") require(module)
require("autostart")
------------------
---- MONITORS ----
------------------
-- See https://wiki.hypr.land/Configuring/Basics/Monitors/
hl.monitor({
output = "HDMI-A-1",
mode = "2560x1440@144",
position = "0x0",
scale = 1,
})
hl.monitor({
output = "DP-3",
mode = "2560x1440@120",
position = "2560x0",
scale = 1,
})
local hyper = "MOD3"
---------------------
---- MY PROGRAMS ----
---------------------
local function uwsm(cmd)
return "uwsm-app -- " .. cmd
end end
-- Set programs that you use
local terminal = uwsm("alacritty")
local fileManager = uwsm("dolphin")
local menu = "hyprlauncher"
-------------------------------
---- ENVIRONMENT VARIABLES ----
-------------------------------
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Environment-variables/
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
-----------------------
----- PERMISSIONS -----
-----------------------
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Permissions/
-- Please note permission changes here require a Hyprland restart and are not applied on-the-fly
-- for security reasons
-- hl.config({
-- ecosystem = {
-- enforce_permissions = true,
-- },
-- })
-- hl.permission("/usr/(bin|local/bin)/grim", "screencopy", "allow")
-- hl.permission("/usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland", "screencopy", "allow")
-- hl.permission("/usr/(bin|local/bin)/hyprpm", "plugin", "allow")
-----------------------
---- LOOK AND FEEL ----
-----------------------
-- Refer to https://wiki.hypr.land/Configuring/Basics/Variables/
hl.config({
general = {
gaps_in = 5,
gaps_out = 20,
border_size = 2,
col = {
active_border = { colors = { "rgba(33ccffee)", "rgba(00ff99ee)" }, angle = 45 },
inactive_border = "rgba(595959aa)",
},
-- Set to true to enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false,
-- Please see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Tearing/ before you turn this on
allow_tearing = false,
layout = "dwindle",
},
decoration = {
rounding = 10,
rounding_power = 2,
-- Change transparency of focused and unfocused windows
active_opacity = 1.0,
inactive_opacity = 1.0,
shadow = {
enabled = true,
range = 4,
render_power = 3,
color = 0xee1a1a1a,
},
blur = {
enabled = true,
size = 3,
passes = 1,
vibrancy = 0.1696,
},
},
animations = {
enabled = true,
},
})
-- Default curves and animations, see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Animations/
hl.curve("easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } })
hl.curve("easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } })
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
hl.curve("almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } })
hl.curve("quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } })
-- Default springs
hl.curve("easy", { type = "spring", mass = 1, stiffness = 238.1191, dampening = 24.21279333 })
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, spring = "easy" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, spring = "easy", style = "popin 87%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" })
hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" })
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
hl.animation({ leaf = "zoomFactor", enabled = true, speed = 7, bezier = "quick" })
-- Ref https://wiki.hypr.land/Configuring/Basics/Workspace-Rules/
-- "Smart gaps" / "No gaps when only"
-- uncomment all if you wish to use that.
-- hl.workspace_rule({ workspace = "w[tv1]", gaps_out = 0, gaps_in = 0 })
-- hl.workspace_rule({ workspace = "f[1]", gaps_out = 0, gaps_in = 0 })
-- hl.window_rule({
-- name = "no-gaps-wtv1",
-- match = { float = false, workspace = "w[tv1]" },
-- border_size = 0,
-- rounding = 0,
-- })
-- hl.window_rule({
-- name = "no-gaps-f1",
-- match = { float = false, workspace = "f[1]" },
-- border_size = 0,
-- rounding = 0,
-- })
-- See https://wiki.hypr.land/Configuring/Layouts/Dwindle-Layout/ for more
hl.config({
dwindle = {
preserve_split = true, -- You probably want this
},
})
-- See https://wiki.hypr.land/Configuring/Layouts/Master-Layout/ for more
hl.config({
master = {
new_status = "master",
},
})
-- See https://wiki.hypr.land/Configuring/Layouts/Scrolling-Layout/ for more
hl.config({
scrolling = {
fullscreen_on_one_column = true,
},
})
----------------
---- MISC ----
----------------
hl.config({
misc = {
force_default_wallpaper = -1, -- Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false, -- If true disables the random hyprland logo / anime girl background. :(
},
})
---------------
---- INPUT ----
---------------
hl.config({
input = {
kb_layout = "us",
kb_variant = "",
kb_model = "",
kb_options = "caps:hyper",
kb_rules = "",
follow_mouse = 1,
sensitivity = 0, -- -1.0 - 1.0, 0 means no modification.
touchpad = {
natural_scroll = false,
},
},
})
hl.gesture({
fingers = 3,
direction = "horizontal",
action = "workspace"
})
-- Example per-device config
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Devices/ for more
hl.device({
name = "epic-mouse-v1",
sensitivity = -0.5,
})
---------------------
---- KEYBINDINGS ----
---------------------
local mainMod = "SUPER" -- Sets "Windows" key as main modifier
-- Example binds, see https://wiki.hypr.land/Configuring/Basics/Binds/ for more
hl.bind(mainMod .. " + Q", hl.dsp.exec_cmd(terminal))
-- closeWindowBind:set_enabled(false)
hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(fileManager))
hl.bind(mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }))
hl.bind(mainMod .. " + R", hl.dsp.exec_cmd(menu))
hl.bind(mainMod .. " + P", hl.dsp.window.pseudo())
hl.bind(mainMod .. " + J", hl.dsp.layout("togglesplit")) -- dwindle only
-- Move focus with mainMod + arrow keys
hl.bind(mainMod .. " + left", hl.dsp.focus({ direction = "left" }))
hl.bind(mainMod .. " + right", hl.dsp.focus({ direction = "right" }))
hl.bind(mainMod .. " + up", hl.dsp.focus({ direction = "up" }))
hl.bind(mainMod .. " + down", hl.dsp.focus({ direction = "down" }))
-- Switch workspaces with mainMod + [0-9]
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }))
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- Scroll through existing workspaces with mainMod + scroll
hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
-- Move/resize windows with mainMod + LMB/RMB and dragging
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- Laptop multimedia keys for volume and LCD brightness
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"),
{ locked = true, repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"),
{ locked = true, repeating = true })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"),
{ locked = true, repeating = true })
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"),
{ locked = true, repeating = true })
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%+"), { locked = true, repeating = true })
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%-"), { locked = true, repeating = true })
-- Requires playerctl
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
hl.bind(hyper .. " + W", hl.dsp.focus({ direction = "u" }))
hl.bind(hyper .. " + A", hl.dsp.focus({ direction = "l" }))
hl.bind(hyper .. " + S", hl.dsp.focus({ direction = "d" }))
hl.bind(hyper .. " + D", hl.dsp.focus({ direction = "r" }))
hl.bind(hyper .. " + CTRL + W", hl.dsp.window.move({ direction = "u" }))
hl.bind(hyper .. " + CTRL + A", hl.dsp.window.move({ direction = "l" }))
hl.bind(hyper .. " + CTRL + S", hl.dsp.window.move({ direction = "d" }))
hl.bind(hyper .. " + CTRL + D", hl.dsp.window.move({ direction = "r" }))
--------------------------------
---- WINDOWS AND WORKSPACES ----
--------------------------------
-- See https://wiki.hypr.land/Configuring/Basics/Window-Rules/
-- and https://wiki.hypr.land/Configuring/Basics/Workspace-Rules/
-- Example window rules that are useful
local suppressMaximizeRule = hl.window_rule({
-- Ignore maximize requests from all apps. You'll probably like this.
name = "suppress-maximize-events",
match = { class = ".*" },
suppress_event = "maximize",
})
-- suppressMaximizeRule:set_enabled(false)
hl.window_rule({
-- Fix some dragging issues with XWayland
name = "fix-xwayland-drags",
match = {
class = "^$",
title = "^$",
xwayland = true,
float = true,
fullscreen = false,
pin = false,
},
no_focus = true,
})
-- Layer rules also return a handle.
-- local overlayLayerRule = hl.layer_rule({
-- name = "no-anim-overlay",
-- match = { namespace = "^my-overlay$" },
-- no_anim = true,
-- })
-- overlayLayerRule:set_enabled(false)
-- Hyprland-run windowrule
hl.window_rule({
name = "move-hyprland-run",
match = { class = "hyprland-run" },
move = "20 monitor_h-120",
float = true,
})
-- For Noctalia Color templates -- For Noctalia Color templates
require("noctalia").apply_theme() require("noctalia").apply_theme()
+6
View File
@@ -2,4 +2,10 @@ theme {
color_scheme = /usr/share/color-schemes/BreezeDark.colors color_scheme = /usr/share/color-schemes/BreezeDark.colors
style = Breeze style = Breeze
icon_theme = breeze-dark icon_theme = breeze-dark
font = Source Han Sans CN
font_size = 11
font_fixed = monospace
font_fixed_size = 11
} }
-80
View File
@@ -1,80 +0,0 @@
local hyper = "MOD3 + "
hl.bind("SUPER + SPACE", function()
hl.dispatch(
hl.dsp.exec_cmd("noctalia msg panel-toggle launcher")
)
end, {
description = "Toggle Noctalia launcher",
})
hl.bind("SUPER + W", hl.dsp.window.close())
for i = 1, 9 do
hl.bind(
"MOD3 + " .. i,
hl.dsp.focus({ workspace = i }),
{ description = "Switch to workspace " .. i }
)
hl.bind(
"MOD3 + CTRL + " .. i,
hl.dsp.window.move({ workspace = i }),
{ description = "Send focused window to " .. i }
)
end
local monitor_dirs = {
W = "u",
A = "l",
S = "d",
D = "r",
}
for key, dir in pairs(monitor_dirs) do
hl.bind(
hyper .. " + SUPER + " .. key,
hl.dsp.workspace.move({ monitor = dir }),
{ description = "Move workspace " .. dir }
)
end
--- Hyprspace
hl.bind(hyper .. "SPACE", function() hl.plugin.hyprtasking.toggle("all") end)
--- floating focus
hl.bind(hyper .. " F", function()
local win = hl.get_active_window()
local mon = hl.get_active_monitor()
if not win or not mon then
return
end
local was_tiled = not win.floating
hl.dispatch(hl.dsp.window.float({
action = "toggle",
}))
-- Only resize when entering floating focus mode.
-- When leaving it, let the tiling layout restore the window.
if was_tiled then
hl.dispatch(hl.dsp.window.resize({
x = math.floor(mon.width * 0.80),
y = math.floor(mon.height * 0.80),
relative = false,
}))
hl.dispatch(hl.dsp.window.center())
hl.dispatch(hl.dsp.window.alter_zorder({
mode = "top",
}))
end
end, {
description = "Toggle focused floating window", })
hl.bind("SUPER + SHIFT + S", hl.plugin.hyprcapture.open, { description = "HyprCapture window capture" })
+10
View File
@@ -0,0 +1,10 @@
-- Small command builders shared by keybindings and autostart.
local M = {}
-- Launch graphical applications through UWSM so they inherit the managed
-- Wayland session environment and systemd scope.
function M.uwsm(command)
return "uwsm-app -- " .. command
end
return M
+25 -5
View File
@@ -1,9 +1,13 @@
-- Generated by Noctalia -- Generated by Noctalia
local primary = "rgb(fff59b)" local primary = "rgb(7aa2f7)"
local surface = "rgb(070722)" local surface = "rgb(1a1b26)"
local secondary = "rgb(a9aefe)" local on_surface = "rgb(c0caf5)"
local error = "rgb(fd4663)" local secondary = "rgb(bb9af7)"
local on_secondary = "rgb(16161e)"
local error = "rgb(f7768e)"
local on_error = "rgb(16161e)"
local shadow = "rgb(15161e)"
local function apply_theme() local function apply_theme()
hl.config({ hl.config({
@@ -13,7 +17,14 @@ local function apply_theme()
inactive_border = surface, inactive_border = surface,
}, },
}, },
decoration = {
shadow = {
color = shadow,
},
glow = {
color = shadow,
},
},
group = { group = {
col = { col = {
border_active = secondary, border_active = secondary,
@@ -23,12 +34,17 @@ local function apply_theme()
}, },
groupbar = { groupbar = {
gradients = true,
col = { col = {
active = secondary, active = secondary,
inactive = surface, inactive = surface,
locked_active = error, locked_active = error,
locked_inactive = surface, locked_inactive = surface,
}, },
text_color = on_secondary,
text_color_inactive = on_surface,
text_color_locked_active = on_error,
text_color_locked_inactive = on_surface,
}, },
}, },
}) })
@@ -38,8 +54,12 @@ return {
colors = { colors = {
primary = primary, primary = primary,
surface = surface, surface = surface,
on_surface = on_surface,
secondary = secondary, secondary = secondary,
on_secondary = on_secondary,
error = error, error = error,
on_error = on_error,
shadow = shadow,
}, },
apply_theme = apply_theme apply_theme = apply_theme
} }
-14
View File
@@ -1,14 +0,0 @@
hl.window_rule({
name = "ueberzugpp-preview",
match = {
class = "ueberzugpp_.*",
},
float = true,
no_initial_focus = true,
border_size = 0,
rounding = 0,
no_shadow = true,
})
-67
View File
@@ -1,67 +0,0 @@
local hyper = "MOD3"
local bili_space = "name:bilibili"
local im_space = "name:im"
-- Master layout configuration
hl.config({
master = {
mfact = 0.80,
orientation = "left",
-- New windows go into the background/slave column,
-- rather than replacing your master.
new_status = "slave",
},
})
-- Dedicated workspace
hl.workspace_rule({
workspace = bili_space,
layout = "master",
layout_opts = {
orientation = "left",
},
persistent = true,
monitor = "DP-3"
})
hl.workspace_rule({
workspace = im_space,
layout = "master",
layout_opts = {
orientation = "left",
},
persistent = true,
monitor = "DP-3"
})
hl.bind(
hyper .. " + F1",
hl.dsp.focus({ workspace = bili_space }),
{ description = "Switch to bilibili workspace" }
)
hl.bind(
hyper .. " + CTRL + F1",
hl.dsp.window.move({ workspace = bili_space }),
{ description = "Send focused window to bilibili workspace" }
)
hl.bind(
hyper .. " + F2",
hl.dsp.focus({ workspace = im_space }),
{ description = "Switch to im workspace" }
)
hl.bind(
hyper .. " + CTRL + F2",
hl.dsp.window.move({ workspace = im_space }),
{ description = "Send focused window to im workspace" }
)
hl.bind(
hyper .. " + M",
hl.dsp.layout("swapwithmaster master ignoremaster"),
{ description = "Promote to master" }
)