Add the installer, uninstaller, and generated docs

This commit is contained in:
2026-09-20 00:19:38 -04:00
parent 0c422e7345
commit 3e65d176e3
100 changed files with 2585 additions and 0 deletions
-5
View File
@@ -1,5 +0,0 @@
-- App-specific tweaks.
local paths = require("default.hypr.paths")
local require_all = require("default.hypr.require_all")
require_all.files(paths.blob_path .. "/default/hypr/apps", "default.hypr.apps")
-1
View File
@@ -1 +0,0 @@
o.window("^(1[p|P]assword)$", { no_screen_share = true, tag = "+floating-window" })
-16
View File
@@ -1,16 +0,0 @@
-- Battle.net launches under Proton; all its windows share class steam_app_battlenet.
-- The actual launcher: float-centered.
o.window({ class = "^steam_app_battlenet$", title = "^Battle\\.net$" }, {
float = true,
center = true,
size = { 1280, 800 },
})
-- Installer: drop decorations and backdrop blur/shadow so the Blizzard chrome
-- isn't framed by the WM.
o.window({ class = "^steam_app_battlenet$", title = "^Battle\\.net Setup$" }, {
decorate = false,
no_blur = true,
no_shadow = true,
})
-6
View File
@@ -1,6 +0,0 @@
o.window("^(Bitwarden)$", { no_screen_share = true, tag = "+floating-window" })
o.window("chrome-nngceckbapebfimnlniiiahkandclblb-Default", {
no_screen_share = true,
tag = "+floating-window",
})
-12
View File
@@ -1,12 +0,0 @@
-- Browser tags and styling.
o.window("((google-)?[cC]hrom(e|ium)|[bB]rave-browser|[mM]icrosoft-edge|Vivaldi-stable|helium)", { tag = "+chromium-based-browser" })
o.window("([fF]irefox|zen|librewolf)", { tag = "+firefox-based-browser" })
o.window({ tag = "chromium-based-browser" }, { tag = "-default-opacity", tile = true, opacity = "1.0 0.985" })
o.window({ tag = "firefox-based-browser" }, { tag = "-default-opacity", opacity = "1.0 0.985" })
-- Video apps: remove chromium browser tag so they don't get opacity applied.
o.window("(^.+-youtube\\.com__.*$|^.+-app\\.zoom\\.us__wc_home.*$)", { tag = "-chromium-based-browser" })
o.window("(^.+-youtube\\.com__.*$|^.+-app\\.zoom\\.us__wc_home.*$)", { tag = "-default-opacity" })
-- Hide screen sharing notification windows.
o.window({ title = ".*is sharing.*" }, { workspace = "special silent" })
-11
View File
@@ -1,11 +0,0 @@
-- DaVinci Resolve window focus handling. Kept fully opaque: the default
-- translucency distorts colour-critical grading work.
o.window(".*[Rr]esolve.*", {
float = true,
stay_focused = true,
tag = "-default-opacity",
opacity = "1 1",
})
o.window({ class = ".*[Rr]esolve.*", title = "^DaVinci Resolve( Studio)? - .+$" }, { fullscreen = true })
o.window({ class = ".*[Rr]esolve.*", title = "^(DaVinci Resolve( Studio)? - .+|Project Manager)$" }, { stay_focused = false })
-1
View File
@@ -1 +0,0 @@
o.window("GeForceNOW", { idle_inhibit = "fullscreen" })
-7
View File
@@ -1,7 +0,0 @@
-- Hermes Desktop's frameless HUD manages its own geometry.
o.window({ class = "^Hermes$", title = "^Hermes HUD$" }, {
tag = "-default-opacity",
float = true,
border_size = 0,
opacity = "1 1",
})
-2
View File
@@ -1,2 +0,0 @@
-- Disable mouse focus (see https://github.com/basecamp/omarchy/pull/5183#issuecomment-4189299971).
o.window("^(jetbrains-.*)$", { no_follow_mouse = true })
-3
View File
@@ -1,3 +0,0 @@
-- Float LocalSend and fzf file picker.
o.window("(Share|localsend)", { float = true, center = true })
o.window("localsend", { size = { 1100, 700 } })
-1
View File
@@ -1 +0,0 @@
o.window("com.moonlight_stream.Moonlight", { fullscreen = true, idle_inhibit = "fullscreen" })
-14
View File
@@ -1,14 +0,0 @@
-- Window and layer rules for the Blob Quickshell surfaces. The
-- shell-wide bar / menu / popouts are layer-shell.
-- Keep the bar instant: no layer-shell fade/slide animation.
hl.layer_rule({ match = { namespace = "blob-bar" }, no_anim = true, animation = "none" })
-- Launcher, image selector, emojis, clipboard overlays, and keyboard-driven
-- panels should pop without compositor layer fades. Panels keep their own
-- QML opacity transition for normal open/close, and skip it for panel handoff.
hl.layer_rule({ match = { namespace = "^(blob-menu|blob-image-selector|blob-emojis|blob-clipboard|blob-keyboard-panel)$" }, no_anim = true, animation = "none" })
-- Dev gallery is the main shell workbench; open it maximized like
-- SUPER+ALT+F so component previews have the whole workspace.
o.window({ class = "^org.quickshell$", title = "^Blob shell dev gallery$" }, { maximize = true })
-24
View File
@@ -1,24 +0,0 @@
-- Picture-in-picture overlays.
o.window({ title = "(Picture.?in.?[Pp]icture)" }, { tag = "+pip" })
o.window({ tag = "pip" }, {
tag = "-default-opacity",
float = true,
pin = true,
size = { 600, 338 },
keep_aspect_ratio = true,
border_size = 0,
opacity = "1 1",
move = { "(monitor_w-window_w-40)", "(monitor_h*0.04)" },
})
-- Google Meet PiP uses the meeting title instead of "Picture-in-Picture".
o.window({ tag = "chromium-based-browser", title = "^Meet - .+" }, {
tag = "-default-opacity",
float = true,
pin = true,
size = { 600, 338 },
keep_aspect_ratio = true,
border_size = 0,
opacity = "1 1",
move = { "(monitor_w-window_w-40)", "(monitor_h-window_h-40)" },
})
-1
View File
@@ -1 +0,0 @@
o.window("qemu", { tag = "-default-opacity", opacity = "1 1" })
-6
View File
@@ -1,6 +0,0 @@
o.window("com.libretro.RetroArch", {
fullscreen = true,
tag = "-default-opacity",
opacity = "1 1",
idle_inhibit = "fullscreen",
})
@@ -1,2 +0,0 @@
-- Remove the 1px border around the slurp region selection used by screenshots.
hl.layer_rule({ match = { namespace = "selection" }, no_anim = true, animation = "none" })
-4
View File
@@ -1,4 +0,0 @@
o.window("steam", { float = true, idle_inhibit = "fullscreen" })
o.window({ class = "steam", title = "Steam" }, { center = true, size = { 1100, 700 } })
o.window("steam.*", { tag = "-default-opacity", opacity = "1 1" })
o.window({ class = "steam", title = "Friends List" }, { size = { 460, 800 } })
-54
View File
@@ -1,54 +0,0 @@
-- Floating windows.
o.window({ tag = "floating-window" }, { float = true })
o.window({ tag = "floating-window" }, { center = true })
o.window({ tag = "floating-window" }, { size = { 875, 600 } })
o.window(
"(org.blob.btop|org.blob.terminal|org.blob.bash|org.codeberg.dnkl.foot|org.gnome.NautilusPreviewer|org.gnome.Evince|Blob|About|TUI.float|imv|mpv)",
{
tag = "+floating-window",
}
)
-- The portal only ever shows dialogs — file pickers, screen shares, permission
-- prompts — so every one of its windows belongs in the floating treatment,
-- whatever the app that asked for it titled it.
o.window("xdg-desktop-portal-gtk", { tag = "+floating-window" })
o.window({
class = "(sublime_text|DesktopEditors|org.gnome.Nautilus)",
title = "^(Open.*Files?|Open [F|f]older.*|Save.*Files?|Save.*As|Save|All Files|.*wants to [open|save].*|[C|c]hoose.*)",
}, { tag = "+floating-window" })
-- The About fastfetch layout needs more columns than the standard float provides.
-- This size only covers the first launch: blob-launch-about measures the
-- rendered content, remembers the size that hugs it, and applies that as its own
-- rule before every later launch.
o.window("org.blob.about", { float = true })
o.window("org.blob.about", { center = true })
o.window("org.blob.about", { size = { 920, 480 } })
-- Fullscreen screensaver.
o.window("org.blob.screensaver", { fullscreen = true })
o.window("org.blob.screensaver", { float = true })
o.window("org.blob.screensaver", { animation = "slide" })
-- No transparency on media windows.
o.window(
"^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$",
{
tag = "-default-opacity",
}
)
o.window(
"^(zoom|vlc|mpv|org.kde.kdenlive|com.obsproject.Studio|com.github.PintaProject.Pinta|imv|org.gnome.NautilusPreviewer)$",
{
opacity = "1 1",
}
)
-- Popped window rounding.
o.window({ tag = "pop" }, { rounding = 8 })
-- Prevent idle while open.
o.window({ tag = "noidle" }, { idle_inhibit = "always" })
-2
View File
@@ -1,2 +0,0 @@
-- Prevent Telegram from stealing focus on new messages.
o.window("org.telegram.desktop", { focus_on_activate = false })
-8
View File
@@ -1,8 +0,0 @@
-- Define terminal tag so themes and bindings can single terminals out. Blob
-- launches TUIs and its own terminal windows under dedicated app-ids
-- (org.blob.btop, org.blob.terminal, TUI.float, ...), so match those too.
-- The class is matched in full, so foot's other app-id needs spelling out.
o.window(
"(Alacritty|kitty|com.mitchellh.ghostty|foot|org\\.codeberg\\.dnkl\\.foot|wezterm|org\\.blob\\..*|TUI\\..*)",
{ tag = "+terminal" }
)
-25
View File
@@ -1,25 +0,0 @@
-- The 8:9 portrait presets scale from monitor height so they occupy the same
-- share of anything from a 1080p monitor to a scaled 6K display.
o.window("^WebcamOverlay-small$", {
size = { "(monitor_h*4/25)", "(monitor_h*9/50)" },
move = { "(monitor_w-monitor_h*4/25-40)", "(monitor_h-monitor_h*9/50-40)" },
})
o.window("^WebcamOverlay-medium$", {
size = { "(monitor_h*2/9)", "(monitor_h/4)" },
move = { "(monitor_w-monitor_h*2/9-40)", "(monitor_h-monitor_h/4-40)" },
})
o.window("^WebcamOverlay-large$", {
size = { "(monitor_h*3/10)", "(monitor_h*27/80)" },
move = { "(monitor_w-monitor_h*3/10-40)", "(monitor_h-monitor_h*27/80-40)" },
})
-- The dedicated app id keeps this out of mpv's generic centered floating rules,
-- so the camera appears at its final corner position.
o.window({ class = "^WebcamOverlay-(small|medium|large)$", title = "^WebcamOverlay$" }, {
tag = "-default-opacity",
float = true,
pin = true,
no_initial_focus = true,
no_dim = true,
opacity = "1 1",
})
-13
View File
@@ -1,13 +0,0 @@
hl.on("hyprland.start", function()
-- Slow app launch fix -- set systemd vars before starting session services.
hl.exec_cmd("systemctl --user import-environment $(env | cut -d'=' -f 1)")
hl.exec_cmd("dbus-update-activation-environment --systemd --all")
hl.exec_cmd("blob-launch-shell")
hl.exec_cmd("blob-powerprofiles-init")
hl.exec_cmd(o.launch("blob-hypr-monitor-watch"))
hl.exec_cmd(o.launch("udiskie --automount --no-notify --no-tray"))
-- Run post-boot hooks after startup config has loaded.
hl.exec_cmd("sleep 2 && blob-hook post-boot")
end)
-4
View File
@@ -1,4 +0,0 @@
local paths = require("default.hypr.paths")
local require_all = require("default.hypr.require_all")
require_all.files(paths.blob_path .. "/default/hypr/bindings", "default.hypr.bindings")
-15
View File
@@ -1,15 +0,0 @@
-- Essential application bindings.
o.bind("SUPER + RETURN", "Terminal", { blob = "terminal" })
o.bind("SUPER + SHIFT + RETURN", "Browser", { blob = "browser" })
o.bind("SUPER + SHIFT + F", "File manager", { blob = "nautilus" })
o.bind("SUPER + ALT + SHIFT + F", "File manager (cwd)", { blob = "nautilus-cwd" })
o.bind("SUPER + SHIFT + B", "Browser", { blob = "browser" })
o.bind("SUPER + SHIFT + ALT + B", "Browser (private)", { blob = "browser --private" })
o.bind("SUPER + SHIFT + N", "Editor", { blob = "editor" })
if o.preinstalled_bindings_enabled() then
o.bind("SUPER + SHIFT + M", "Music", { blob = "spotify" })
o.bind("SUPER + SHIFT + D", "Docker", { tui = "blob-launch-docker-tui" })
o.bind("SUPER + SHIFT + G", "Signal", { blob = "signal" })
o.bind("SUPER + SHIFT + O", "Obsidian", { launch = "obsidian", focus = "^obsidian$" })
end
-48
View File
@@ -1,48 +0,0 @@
-- Send with explicit mods to the focused surface by omitting the window target,
-- so universal clipboard shortcuts reach both normal windows and focused
-- layer-shell surfaces such as Blob panels. A virtual keyboard (wtype) won't
-- do: the physically held SUPER merges into the injected chord at the seat.
-- The down/up split works around Hyprland send_shortcut sometimes leaving
-- synthetic key state stuck/repeating.
-- https://github.com/hyprwm/Hyprland/discussions/14099
local function send_shortcut_once(mods, key)
return function()
hl.dispatch(hl.dsp.send_key_state({ mods = mods, key = key, state = "down" }))
hl.timer(function()
hl.dispatch(hl.dsp.send_key_state({ mods = mods, key = key, state = "up" }))
end, { timeout = 50, type = "oneshot" })
end
end
-- Lean on the terminal tag from default/hypr/apps/terminals.lua so there's one
-- definition of what counts as a terminal. Dynamic tags carry a trailing "*".
local function active_window_is_terminal()
local window = hl.get_active_window()
if not window then
return false
end
for _, tag in ipairs(window.tags or {}) do
if tag:gsub("%*$", "") == "terminal" then
return true
end
end
return false
end
local function universal_clipboard_shortcut(default_mods, default_key, terminal_mods, terminal_key)
return function()
if active_window_is_terminal() then
send_shortcut_once(terminal_mods, terminal_key)()
else
send_shortcut_once(default_mods, default_key)()
end
end
end
o.bind("SUPER + C", "Universal copy", universal_clipboard_shortcut("CTRL", "C", "CTRL", "Insert"))
o.bind("SUPER + V", "Universal paste", universal_clipboard_shortcut("CTRL", "V", "SHIFT", "Insert"))
o.bind("SUPER + X", "Universal cut", send_shortcut_once("CTRL", "X"))
o.bind("SUPER + CTRL + V", "Clipboard manager", "blob-shell shell toggle blob.clipboard")
-34
View File
@@ -1,34 +0,0 @@
-- Volume, brightness, keyboard backlight, and touchpad controls.
o.bind("XF86AudioRaiseVolume", "Volume up", "blob-audio-volume raise", { locked = true, repeating = true })
o.bind("XF86AudioLowerVolume", "Volume down", "blob-audio-volume lower", { locked = true, repeating = true })
o.bind("XF86AudioMute", "Mute", "blob-audio-volume mute-toggle", { locked = true })
o.bind("XF86AudioMicMute", "Mute microphone", "blob-audio-input-mute", { locked = true })
o.bind("XF86MonBrightnessUp", "Brightness up", "blob-brightness-display +5%", { locked = true, repeating = true })
o.bind("XF86MonBrightnessDown", "Brightness down", "blob-brightness-display 5%-", { locked = true, repeating = true })
o.bind("SHIFT + XF86MonBrightnessUp", "Brightness maximum", "blob-brightness-display 100%", { locked = true, repeating = true })
o.bind("SHIFT + XF86MonBrightnessDown", "Brightness minimum", "blob-brightness-display 1%", { locked = true, repeating = true })
o.bind("XF86KbdBrightnessUp", "Keyboard brightness up", "blob-brightness-keyboard up", { locked = true, repeating = true })
o.bind("XF86KbdBrightnessDown", "Keyboard brightness down", "blob-brightness-keyboard down", { locked = true, repeating = true })
o.bind("XF86KbdLightOnOff", "Keyboard backlight cycle", "blob-brightness-keyboard cycle", { locked = true })
o.bind_toggle("XF86TouchpadToggle", "Toggle touchpad", "touchpad", { locked = true })
o.bind("XF86TouchpadOn", "Enable touchpad", "blob-toggle-touchpad on", { locked = true })
o.bind("XF86TouchpadOff", "Disable touchpad", "blob-toggle-touchpad off", { locked = true })
-- Precise volume and brightness controls.
o.bind("ALT + XF86AudioRaiseVolume", "Volume up precise", "blob-audio-volume +1", { locked = true, repeating = true })
o.bind("ALT + XF86AudioLowerVolume", "Volume down precise", "blob-audio-volume -1", { locked = true, repeating = true })
o.bind("ALT + XF86MonBrightnessUp", "Brightness up precise", "blob-brightness-display +1%", { locked = true, repeating = true })
o.bind("ALT + XF86MonBrightnessDown", "Brightness down precise", "blob-brightness-display 1%-", { locked = true, repeating = true })
-- Media controls.
o.bind("XF86AudioNext", "Next track", "blob-shell media next", { locked = true })
o.bind("ALT + XF86AudioPlay", "Next track", "blob-shell media next", { locked = true })
o.bind("XF86AudioPause", "Pause", "blob-shell media playPause", { locked = true })
o.bind("XF86AudioPlay", "Play", "blob-shell media playPause", { locked = true })
o.bind("XF86AudioPrev", "Previous track", "blob-shell media previous", { locked = true })
o.bind("ALT + SHIFT + XF86AudioPlay", "Previous track", "blob-shell media previous", { locked = true })
o.bind("XF86Eject", "Eject media", "eject", { locked = true })
o.bind("SHIFT + XF86AudioMute", "Switch audio output", "blob-audio-sink-switch", { locked = true })
o.bind("SHIFT + XF86AudioPause", "Switch media source", "blob-audio-source-switch", { locked = true })
o.bind("SHIFT + XF86AudioPlay", "Switch media source", "blob-audio-source-switch", { locked = true })
-95
View File
@@ -1,95 +0,0 @@
o.bind("SUPER + W", "Close window", hl.dsp.window.close())
o.bind("CTRL + ALT + DELETE", "Close all windows", "blob-hypr-window-close-all")
o.bind("SUPER + J", "Toggle window split", hl.dsp.layout("togglesplit"))
o.bind("SUPER + P", "Pseudo window", hl.dsp.window.pseudo())
o.bind("SUPER + T", "Toggle window floating/tiling", hl.dsp.window.float({ action = "toggle" }))
o.bind("SUPER + F", "Full screen", hl.dsp.window.fullscreen({ mode = "fullscreen" }))
o.bind("SUPER + CTRL + F", "Tiled full screen", "blob-hypr-window-tiled-fullscreen-toggle")
o.bind("SUPER + ALT + F", "Full width", hl.dsp.window.fullscreen({ mode = "maximized" }))
o.bind("SUPER + O", "Pop window out (float & pin)", "blob-hypr-window-pop")
o.bind("SUPER + ALT + Home", "Save window width", "blob-hypr-window-width save")
o.bind("SUPER + Home", "Restore window width", "blob-hypr-window-width restore")
o.bind("SUPER + L", "Toggle workspace layout", "blob-hypr-workspace-layout-toggle")
o.bind("SUPER + LEFT", "Focus on left window", hl.dsp.focus({ direction = "l" }))
o.bind("SUPER + RIGHT", "Focus on right window", hl.dsp.focus({ direction = "r" }))
o.bind("SUPER + UP", "Focus on above window", hl.dsp.focus({ direction = "u" }))
o.bind("SUPER + DOWN", "Focus on below window", hl.dsp.focus({ direction = "d" }))
for workspace = 1, 10 do
local key = "code:" .. tostring(workspace + 9)
o.bind("SUPER + " .. key, "Switch to workspace " .. workspace, hl.dsp.focus({ workspace = tostring(workspace) }))
o.bind("SUPER + SHIFT + " .. key, "Move window to workspace " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace) }))
o.bind("SUPER + SHIFT + ALT + " .. key, "Move window silently to workspace " .. workspace, hl.dsp.window.move({ workspace = tostring(workspace), follow = false }))
end
o.bind("SUPER + S", "Toggle scratchpad", hl.dsp.workspace.toggle_special("scratchpad"))
o.bind("SUPER + ALT + S", "Move window to scratchpad", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
o.bind("SUPER + TAB", "Next workspace", hl.dsp.focus({ workspace = "e+1" }))
o.bind("SUPER + SHIFT + TAB", "Previous workspace", hl.dsp.focus({ workspace = "e-1" }))
o.bind("SUPER + CTRL + TAB", "Former workspace", hl.dsp.focus({ workspace = "previous" }))
o.bind("SUPER + SHIFT + ALT + LEFT", "Move workspace to left monitor", hl.dsp.workspace.move({ monitor = "l" }))
o.bind("SUPER + SHIFT + ALT + RIGHT", "Move workspace to right monitor", hl.dsp.workspace.move({ monitor = "r" }))
o.bind("SUPER + SHIFT + ALT + UP", "Move workspace to up monitor", hl.dsp.workspace.move({ monitor = "u" }))
o.bind("SUPER + SHIFT + ALT + DOWN", "Move workspace to down monitor", hl.dsp.workspace.move({ monitor = "d" }))
o.bind("SUPER + SHIFT + LEFT", "Swap window to the left", hl.dsp.window.swap({ direction = "l" }))
o.bind("SUPER + SHIFT + RIGHT", "Swap window to the right", hl.dsp.window.swap({ direction = "r" }))
o.bind("SUPER + SHIFT + UP", "Swap window up", hl.dsp.window.swap({ direction = "u" }))
o.bind("SUPER + SHIFT + DOWN", "Swap window down", hl.dsp.window.swap({ direction = "d" }))
o.bind("ALT + TAB", "Focus on next window", hl.dsp.window.cycle_next())
o.bind("ALT + SHIFT + TAB", "Focus on previous window", hl.dsp.window.cycle_next({ next = false }))
o.bind("ALT + TAB", "Reveal active window on top", hl.dsp.window.bring_to_top())
o.bind("ALT + SHIFT + TAB", "Reveal active window on top", hl.dsp.window.bring_to_top())
o.bind("CTRL + ALT + TAB", "Focus on next monitor", hl.dsp.focus({ monitor = "+1" }))
o.bind("CTRL + ALT + SHIFT + TAB", "Focus on previous monitor", hl.dsp.focus({ monitor = "-1" }))
o.bind("SUPER + code:20", "Expand window left", hl.dsp.window.resize({ x = -100, y = 0, relative = true }))
o.bind("SUPER + code:21", "Shrink window left", hl.dsp.window.resize({ x = 100, y = 0, relative = true }))
o.bind("SUPER + SHIFT + code:20", "Shrink window up", hl.dsp.window.resize({ x = 0, y = -100, relative = true }))
o.bind("SUPER + SHIFT + code:21", "Expand window down", hl.dsp.window.resize({ x = 0, y = 100, relative = true }))
o.bind("SUPER + ALT + code:20", "Expand window left a little", hl.dsp.window.resize({ x = -25, y = 0, relative = true }))
o.bind("SUPER + ALT + code:21", "Shrink window left a little", hl.dsp.window.resize({ x = 25, y = 0, relative = true }))
o.bind("SUPER + SHIFT + ALT + code:20", "Shrink window up a little", hl.dsp.window.resize({ x = 0, y = -25, relative = true }))
o.bind("SUPER + SHIFT + ALT + code:21", "Expand window down a little", hl.dsp.window.resize({ x = 0, y = 25, relative = true }))
o.bind("SUPER + CTRL + code:20", "Expand window left a lot", hl.dsp.window.resize({ x = -300, y = 0, relative = true }))
o.bind("SUPER + CTRL + code:21", "Shrink window left a lot", hl.dsp.window.resize({ x = 300, y = 0, relative = true }))
o.bind("SUPER + CTRL + SHIFT + code:20", "Shrink window up a lot", hl.dsp.window.resize({ x = 0, y = -300, relative = true }))
o.bind("SUPER + CTRL + SHIFT + code:21", "Expand window down a lot", hl.dsp.window.resize({ x = 0, y = 300, relative = true }))
o.bind("SUPER + mouse_down", "Scroll active workspace forward", hl.dsp.focus({ workspace = "e+1" }))
o.bind("SUPER + mouse_up", "Scroll active workspace backward", hl.dsp.focus({ workspace = "e-1" }))
o.bind("SUPER + mouse:272", "Move window", hl.dsp.window.drag(), { mouse = true })
o.bind("SUPER + mouse:273", "Resize window", hl.dsp.window.resize(), { mouse = true })
o.bind("SUPER + G", "Toggle window grouping", hl.dsp.group.toggle())
o.bind("SUPER + ALT + G", "Move active window out of group", hl.dsp.window.move({ out_of_group = true }))
o.bind("SUPER + ALT + LEFT", "Move window to group on left", hl.dsp.window.move({ into_group = "l" }))
o.bind("SUPER + ALT + RIGHT", "Move window to group on right", hl.dsp.window.move({ into_group = "r" }))
o.bind("SUPER + ALT + UP", "Move window to group on top", hl.dsp.window.move({ into_group = "u" }))
o.bind("SUPER + ALT + DOWN", "Move window to group on bottom", hl.dsp.window.move({ into_group = "d" }))
o.bind("SUPER + ALT + TAB", "Next window in group", hl.dsp.group.next())
o.bind("SUPER + ALT + SHIFT + TAB", "Previous window in group", hl.dsp.group.prev())
o.bind("SUPER + CTRL + LEFT", "Move grouped window focus left", hl.dsp.group.prev())
o.bind("SUPER + CTRL + RIGHT", "Move grouped window focus right", hl.dsp.group.next())
o.bind("SUPER + ALT + mouse_down", "Next window in group", hl.dsp.group.next())
o.bind("SUPER + ALT + mouse_up", "Previous window in group", hl.dsp.group.prev())
for index = 1, 5 do
o.bind("SUPER + ALT + code:" .. tostring(index + 9), "Switch to group window " .. index, hl.dsp.group.active({ index = index }))
end
o.bind("SUPER + SLASH", "Monitor scaling up", "blob-hypr-monitor-scaling up")
o.bind("SUPER + ALT + SLASH", "Monitor scaling down", "blob-hypr-monitor-scaling down")
-121
View File
@@ -1,121 +0,0 @@
o.bind("SUPER + SPACE", "Blob menu", "blob-menu toggle")
o.bind("SUPER + ALT + SPACE", "Apps menu", "blob-menu toggle apps")
o.bind("SUPER + CTRL + E", "Emojis", "blob-shell shell toggle blob.emojis")
o.bind("SUPER + CTRL + C", "Capture menu", "blob-menu toggle capture")
o.bind("SUPER + CTRL + O", "Toggle menu", "blob-menu toggle toggle")
o.bind("SUPER + CTRL + H", "Hardware menu", "blob-menu toggle hardware")
o.bind("SUPER + SHIFT + code:201", "Blob menu", "blob-menu toggle root")
o.bind("SUPER + ESCAPE", "System menu", "blob-menu toggle system")
o.bind("XF86PowerOff", "Power menu", "blob-menu toggle system", { locked = true })
o.bind("SUPER + K", "Keybindings", "blob-menu-keybindings")
o.bind_toggle("SUPER + SHIFT + SPACE", "Toggle top bar", "bar")
o.bind("SUPER + CTRL + SPACE", "Background switcher", "blob-menu toggle background")
o.bind("SUPER + SHIFT + CTRL + SPACE", "Theme menu", "blob-menu toggle theme")
o.bind("SUPER + BACKSPACE", "Toggle window transparency", "blob-hypr-window-transparency-toggle")
o.bind("SUPER + SHIFT + BACKSPACE", "Toggle window gaps", "blob-hypr-window-gaps-toggle")
o.bind("SUPER + CTRL + BACKSPACE", "Toggle single-window square aspect", "blob-hypr-window-single-square-aspect-toggle")
-- xkbcommon names the comma keysym "comma"; the upper-case "COMMA" does not match.
o.bind("SUPER + comma", "Dismiss last notification", "blob-shell notifications dismissOne")
o.bind("SUPER + SHIFT + comma", "Dismiss all notifications", "blob-shell notifications dismissAll")
o.bind_toggle("SUPER + CTRL + comma", "Toggle silencing notifications", "notification-silencing")
o.bind("SUPER + ALT + comma", "Invoke last notification", "blob-shell notifications invokeLast")
o.bind("SUPER + SHIFT + ALT + comma", "Open notification history", "blob-shell notifications showHistory")
o.bind_toggle("SUPER + CTRL + I", "Toggle locking on idle", "idle")
o.bind_toggle("SUPER + CTRL + N", "Toggle nightlight", "nightlight")
o.bind("SUPER + CTRL + Delete", "Toggle laptop display", "blob-hypr-monitor-internal toggle")
o.bind("SUPER + CTRL + ALT + Delete", "Toggle laptop display mirroring", "blob-hypr-monitor-mirror toggle")
o.bind("switch:on:Lid Switch", nil, "blob-system-lid", { locked = true })
o.bind("switch:off:Lid Switch", nil, "blob-hypr-monitor-clamshell", { locked = true })
o.bind("PRINT", "Screenshot", "blob-capture-screenshot")
o.bind("ALT + PRINT", "Screenrecording", "blob-capture-record --stop-recording || blob-menu toggle trigger.capture.screenrecord")
o.bind("SUPER + ALT + code:34", "Make webcam overlay smaller", "blob-capture-webcam-resize smaller")
o.bind("SUPER + ALT + code:35", "Make webcam overlay larger", "blob-capture-webcam-resize larger")
o.bind("SUPER + PRINT", "Color picker", "pkill hyprpicker || hyprpicker -a")
o.bind("SUPER + CTRL + PRINT", "Extract text (OCR) from screenshot", "blob-capture-text")
-- Keyboard control for the slurp region picker (see blob-capture-region).
-- The binds live exactly as long as a selection layer is on screen (slurp
-- opens one per monitor), so they cannot leak or get stuck.
-- Unbinding by key would take a same-key binding out of the user's own config
-- with it, so each handle is kept and removed individually.
local selection_layers = 0
local selection_binds = {}
hl.on("layer.opened", function(layer)
if layer.namespace == "selection" then
selection_layers = selection_layers + 1
if selection_layers == 1 then
selection_binds = {
hl.bind("RETURN", hl.dsp.exec_cmd("blob-capture-region --take-window"), { description = "Capture highlighted window" }),
hl.bind("CTRL + RETURN", hl.dsp.exec_cmd("blob-capture-region --take-fullscreen"), { description = "Capture entire screen" }),
hl.bind("TAB", hl.dsp.exec_cmd("blob-capture-region --select-window next"), { description = "Select next window to capture" }),
hl.bind("CTRL + TAB", hl.dsp.exec_cmd("blob-capture-region --select-window prev"), { description = "Select previous window to capture" }),
}
for _, direction in ipairs({ "left", "right", "up", "down" }) do
table.insert(
selection_binds,
hl.bind(direction:upper(), hl.dsp.exec_cmd("blob-capture-region --select-window " .. direction), { description = "Select window to capture" })
)
end
end
end
end)
hl.on("layer.closed", function(layer)
if layer.namespace == "selection" and selection_layers > 0 then
selection_layers = selection_layers - 1
if selection_layers == 0 then
for _, keybind in ipairs(selection_binds) do
keybind:unbind()
end
selection_binds = {}
end
end
end)
o.bind("SUPER + CTRL + S", "Share", "blob-menu toggle share")
o.bind("SUPER + CTRL + PERIOD", "Transcode", "blob-transcode")
o.bind("SUPER + CTRL + R", "Set reminder", "blob-menu toggle reminder-set")
o.bind("SUPER + CTRL + ALT + R", "Show reminders", "blob-reminder show")
o.bind("SUPER + SHIFT + CTRL + R", "Clear reminders", "blob-reminder clear")
o.bind("SUPER + CTRL + ALT + T", "Show time", "blob-notification-time")
o.bind("SUPER + CTRL + ALT + B", "Show battery remaining", "blob-notification-battery")
o.bind("SUPER + CTRL + ALT + W", "Toggle weather", "blob-notification-weather")
o.bind("SUPER + CTRL + A", "Audio", "blob-shell shell toggle blob.audio")
o.bind("SUPER + CTRL + B", "Bluetooth", "blob-shell shell toggle blob.bluetooth")
o.bind("SUPER + CTRL + D", "Display", "blob-shell shell toggle blob.monitor")
o.bind("SUPER + CTRL + ALT + D", "Calendar", "blob-shell shell toggle blob.clock")
o.bind("SUPER + CTRL + W", "Network", "blob-shell shell toggle blob.network")
o.bind("SUPER + CTRL + P", "Power", "blob-shell shell toggle blob.power")
o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
-- The letters above name a panel; the numbers count them. 1 is the leftmost
-- panel in the bar's right section, and a widget with no panel of its own (the
-- tray) is not counted, so the number matches the icon a user would point at.
-- A bar with fewer panels than this leaves the tail of the range doing nothing.
for panel = 1, 9 do
o.bind(
"SUPER + CTRL + code:" .. tostring(panel + 9),
"Bar panel " .. panel,
"blob-shell -q shell togglePanelAt right " .. panel
)
end
o.bind("SUPER + CTRL + Z", "Zoom in", function()
local zoom = hl.get_config("cursor.zoom_factor") or 1
hl.config({ cursor = { zoom_factor = zoom + 1 } })
end)
o.bind("SUPER + CTRL + ALT + Z", "Reset zoom", function()
hl.config({ cursor = { zoom_factor = 1 } })
end)
o.bind("SUPER + CTRL + L", "Lock system", "blob-system-lock")
-21
View File
@@ -1,21 +0,0 @@
-- Blob Hyprland setup: helpers, defaults, and current theme overrides.
require("default.hypr.helpers")
local require_optional = require("default.hypr.require_optional")
-- Use Blob defaults, but don't edit these directly.
require("default.hypr.autostart")
if _G.blob_default_bindings ~= false then
require("default.hypr.bindings.media")
require("default.hypr.bindings.clipboard")
require("default.hypr.bindings.tiling")
require("default.hypr.bindings.utilities")
require_optional.module("default.hypr.bindings.applications")
end
require("default.hypr.envs")
require("default.hypr.looknfeel")
require("default.hypr.input")
require("default.hypr.windows")
-- Current theme overrides.
require_optional.module("blob.current.theme.hyprland")
-39
View File
@@ -1,39 +0,0 @@
-- Hyprland bootstrap for Blob's Lua module path.
local home = os.getenv("HOME")
local reload_prefixes = {
"default.hypr",
"hypr",
"blob.current.theme",
}
local function should_reload_module(module)
for _, prefix in ipairs(reload_prefixes) do
if module == prefix or module:sub(1, #prefix + 1) == prefix .. "." then
return true
end
end
return false
end
local modules_to_reload = {}
for module in pairs(package.loaded) do
if should_reload_module(module) then
table.insert(modules_to_reload, module)
end
end
for _, module in ipairs(modules_to_reload) do
package.loaded[module] = nil
end
-- Load generated state from ~/.local/state, user modules from ~/.config, and
-- Blob defaults from $BLOB_PATH.
package.path = home
.. "/.local/state/?.lua;"
.. home
.. "/.config/?.lua;"
.. (os.getenv("BLOB_PATH") or (home .. "/.local/share/blob"))
.. "/?.lua;"
.. package.path
-21
View File
@@ -1,21 +0,0 @@
-- Disable a Hyprland input device whose name was stored as data, not Lua.
-- Device names come from USB descriptors and must never be loaded as code.
local paths = require("default.hypr.paths")
return function(kind)
-- Hardcoded to ~/.local/state to match blob-toggle-input and the
-- sibling bash toggle tools, which all write there regardless of
-- XDG_STATE_HOME.
local file = io.open(paths.home .. "/.local/state/blob/toggles/hypr/" .. kind .. "-disabled-name", "r")
if not file then
return
end
local name = file:read("*l")
file:close()
if name and name ~= "" then
hl.device({ name = name, enabled = false })
end
end
-49
View File
@@ -1,49 +0,0 @@
local paths = require("default.hypr.paths")
local require_optional = require("default.hypr.require_optional")
-- GUM environment variables for styling purposes.
require_optional.module("blob.current.theme.gum_env")
-- Cursor size.
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
-- Force all apps to use Wayland.
hl.env("GDK_BACKEND", "wayland,x11,*")
hl.env("QT_QPA_PLATFORM", "wayland;xcb")
hl.env("QT_QPA_PLATFORMTHEME", "gtk3")
hl.env("MOZ_ENABLE_WAYLAND", "1")
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "wayland")
hl.env("OZONE_PLATFORM", "wayland")
hl.env("XDG_SESSION_TYPE", "wayland")
-- Allow better support for screen sharing (Google Meet, Discord, etc).
hl.env("XDG_CURRENT_DESKTOP", "Hyprland")
hl.env("XDG_SESSION_DESKTOP", "Hyprland")
-- Use XCompose file.
hl.env("XCOMPOSEFILE", paths.home .. "/.XCompose")
-- hyprctl setenv doesn't reach keybind dispatcher env; use hl.env.
hl.env("BLOB_PATH", paths.blob_path)
local bin_dir = paths.blob_path .. "/bin"
local kept = {}
for entry in (os.getenv("PATH") or "/usr/local/bin:/usr/bin"):gmatch("[^:]+") do
if entry ~= bin_dir then table.insert(kept, entry) end
end
table.insert(kept, 1, bin_dir)
hl.env("PATH", table.concat(kept, ":"))
-- Hardware-specific environment.
require("default.hypr.nvidia")
hl.config({
xwayland = {
force_zero_scaling = true,
},
ecosystem = {
no_update_news = true,
},
})
-154
View File
@@ -1,154 +0,0 @@
-- Shared helpers for Hyprland Lua configuration.
o = o or {}
local function shell_quote(value)
return "'" .. tostring(value):gsub("'", "'\\''") .. "'"
end
o.shell_quote = shell_quote
local function file_exists(path)
local file = io.open(path, "r")
if file then
file:close()
return true
end
return false
end
-- Hyprland reaps its own children, so os.execute() can't retrieve an exit status
-- from inside the compositor. Read a marker off stdout instead.
function o.shell_succeeds(command)
-- Subshell, so the redirection covers every command rather than binding to
-- the last one and letting an earlier one write its own OK into the pipe.
local pipe = io.popen("( " .. command .. " ) >/dev/null 2>&1 && echo OK")
if not pipe then
return false
end
local output = pipe:read("*a") or ""
pipe:close()
return output:find("OK", 1, true) ~= nil
end
function o.cmd_present(command)
if command:find("/", 1, true) then
return file_exists(command)
end
local path = os.getenv("PATH") or "/usr/local/bin:/usr/bin"
for directory in (path .. ":"):gmatch("([^:]*):") do
if file_exists((directory ~= "" and directory or ".") .. "/" .. command) then
return true
end
end
return false
end
function o.cmd_missing(command)
return not o.cmd_present(command)
end
local function command_from(value, description)
if type(value) ~= "table" then
return value
end
if value.blob then
return "blob-launch-" .. value.blob
elseif value.focus and value.launch then
return o.launch_sole(value.focus, value.launch)
elseif value.launch then
return o.launch(value.launch)
elseif value.webapp then
if value.focus then
return o.launch_webapp_sole(description, value.webapp)
else
return o.launch_webapp(value.webapp)
end
elseif value.tui then
if value.focus then
return "blob-launch-or-focus-tui " .. shell_quote(value.tui)
else
return "blob-launch-tui " .. shell_quote(value.tui)
end
end
return value
end
function o.preinstalled_bindings_enabled()
if _G.blob_preinstalled_bindings ~= nil then
return _G.blob_preinstalled_bindings == true
end
return not file_exists((os.getenv("HOME") or "") .. "/.local/state/blob/preinstalls-removed")
end
function o.bind(keys, description, dispatcher, options)
local opts = options or {}
if description then
opts.description = description
end
dispatcher = command_from(dispatcher, description)
if type(dispatcher) == "string" then
dispatcher = hl.dsp.exec_cmd(dispatcher)
end
hl.bind(keys, dispatcher, opts)
end
function o.launch(command)
return "uwsm-app -- " .. command
end
function o.exec_on_start(command)
hl.on("hyprland.start", function()
hl.exec_cmd(command)
end)
end
function o.launch_on_start(command)
o.exec_on_start(o.launch(command))
end
function o.launch_webapp(url)
return "blob-launch-webapp " .. shell_quote(url)
end
function o.launch_webapp_sole(name, url)
return "blob-launch-or-focus-webapp " .. shell_quote(name) .. " " .. shell_quote(url)
end
function o.launch_sole(match, command)
return "blob-launch-or-focus " .. shell_quote(match) .. " " .. shell_quote(o.launch(command))
end
function o.bind_toggle(keys, description, toggle, options)
o.bind(keys, description, "blob-toggle-" .. toggle, options)
end
function o.notify(message)
return "blob-notify-send -u low " .. shell_quote(message)
end
function o.window(match, rules)
rules.match = rules.match or {}
if type(match) == "string" then
rules.match.class = match
else
for key, value in pairs(match) do
rules.match[key] = value
end
end
hl.window_rule(rules)
end
-79
View File
@@ -1,79 +0,0 @@
-- https://wiki.hypr.land/Configuring/Basics/Variables/#input
local function read_vconsole()
local values = {}
local file = io.open("/etc/vconsole.conf", "r")
if not file then
return values
end
for line in file:lines() do
local key, value = line:match("^%s*([%w_]+)%s*=%s*(.-)%s*$")
if key and value then
value = value:gsub("%s+#.*$", "")
value = value:gsub('^"(.*)"$', "%1")
value = value:gsub("^'(.*)'$", "%1")
values[key] = value
end
end
file:close()
return values
end
-- Layouts that can't type Latin letters. Keep in sync with the list in
-- etc/mkinitcpio.conf.d/blob_hooks.conf.
local non_latin_layouts =
" af am ara bd bg by et ge gr il in iq ir kg kh kz la lk mk mm mn mv np rs ru sy th tj ua "
local vconsole = read_vconsole()
local kb_layout = vconsole.XKBLAYOUT or "us"
local kb_variant = vconsole.XKBVARIANT or ""
-- CapsLock is the compose key, so Caps Lock itself has to live somewhere else.
-- Both Shifts together is the usual home for it, but it's easy to hit by
-- accident while typing. The _cancel variant sets Caps Lock the same way and
-- releases it on the next lone Shift, so a misfire clears itself.
local kb_options = "compose:caps,shift:both_capslock_cancel"
-- Hyprland resolves keybindings against the first entry in kb_layout, not the
-- layout that's currently active, so Blob's Latin-keysym bindings (SUPER + W
-- and friends) only fire when a Latin layout leads. Installing with a non-Latin
-- one would otherwise leave the desktop unusable.
if non_latin_layouts:find(" " .. kb_layout:match("^[^,]*") .. " ", 1, true) then
kb_layout = "us," .. kb_layout
kb_variant = "," .. kb_variant
-- Reach the original layout with Left Alt + Right Alt.
kb_options = kb_options .. ",grp:alts_toggle"
end
hl.config({
input = {
kb_layout = kb_layout,
kb_variant = kb_variant,
kb_model = "",
kb_options = kb_options,
kb_rules = "",
follow_mouse = 1,
sensitivity = 0,
repeat_rate = 40,
repeat_delay = 250,
numlock_by_default = true,
touchpad = {
natural_scroll = false,
clickfinger_behavior = true,
scroll_factor = 0.4,
},
},
misc = {
key_press_enables_dpms = true,
mouse_move_enables_dpms = true,
},
})
-- Scroll nicely in the terminal.
o.window("(Alacritty|kitty|foot)", { scroll_touchpad = 1.5 })
o.window("com.mitchellh.ghostty", { scroll_touchpad = 0.2 })
-126
View File
@@ -1,126 +0,0 @@
-- Refer to https://wiki.hypr.land/Configuring/Basics/Variables/
local active_border_color = { colors = { "rgba(33ccffee)", "rgba(00ff99ee)" }, angle = 45 }
local inactive_border_color = "rgba(595959aa)"
hl.config({
general = {
gaps_in = 5,
gaps_out = 10,
border_size = 2,
col = {
active_border = active_border_color,
inactive_border = inactive_border_color,
},
resize_on_border = false,
allow_tearing = false,
layout = "dwindle",
},
decoration = {
rounding = 0,
shadow = {
enabled = false,
},
blur = {
enabled = false,
},
},
group = {
col = {
border_active = active_border_color,
border_inactive = inactive_border_color,
},
groupbar = {
font_size = 12,
font_family = "monospace",
font_weight_active = "ultraheavy",
font_weight_inactive = "normal",
indicator_height = 1,
indicator_gap = 5,
height = 22,
gaps_in = 5,
gaps_out = 0,
text_color = "rgb(ffffff)",
text_color_inactive = "rgba(ffffff90)",
col = {
active = "rgba(00000040)",
inactive = "rgba(00000020)",
},
gradients = true,
gradient_rounding = 0,
gradient_round_only_edges = false,
},
},
animations = {
enabled = true,
},
})
-- Default 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.0 } } })
hl.curve("quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } })
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 = 3.79, bezier = "easeOutQuint" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", 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 = "fadeSwitch", enabled = false })
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 = false })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "easeOutQuint", style = "slidevert" })
hl.config({
dwindle = {
preserve_split = true,
force_split = 2,
},
scrolling = {
column_width = 0.49,
},
master = {
new_status = "master",
},
misc = {
disable_hyprland_logo = true,
disable_splash_rendering = true,
disable_scale_notification = true,
focus_on_activate = true,
anr_missed_pings = 3,
on_focus_under_fullscreen = 1,
initial_workspace_tracking = 0,
-- Let a fresh shell re-acquire the session lock after the lock client
-- died, so blob-shell-restart can recover the LOCK failsafe.
allow_session_lock_restore = true,
},
cursor = {
hide_on_key_press = true,
warp_on_change_workspace = 1,
},
binds = {
hide_special_on_workspace_change = true,
},
})
-19
View File
@@ -1,19 +0,0 @@
local paths = require("default.hypr.paths")
local nvidia = paths.blob_path .. "/bin/blob-hw-nvidia"
local nvidia_gsp = paths.blob_path .. "/bin/blob-hw-nvidia-gsp"
local nvidia_without_gsp = paths.blob_path .. "/bin/blob-hw-nvidia-without-gsp"
-- These detectors read cached sysfs IDs rather than shelling out to lspci.
-- lspci reads PCI config space, which resumes a runtime-suspended GPU, and on a
-- hybrid laptop that wake alone outlasts Hyprland's 1.5s config reload budget.
if o.shell_succeeds(o.shell_quote(nvidia)) then
if o.shell_succeeds(o.shell_quote(nvidia_gsp)) then
hl.env("NVD_BACKEND", "direct")
hl.env("LIBVA_DRIVER_NAME", "nvidia")
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
elseif o.shell_succeeds(o.shell_quote(nvidia_without_gsp)) then
hl.env("NVD_BACKEND", "egl")
hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
end
end
-22
View File
@@ -1,22 +0,0 @@
-- Shared path constants for Blob's Hyprland Lua modules.
-- Lua files loaded with require() have separate local scopes, so modules that
-- need these paths import this table instead of repeating os.getenv() lookups.
local home = os.getenv("HOME")
-- A variable that is set but empty means "unset" (XDG Base Directory spec);
-- bash's ${VAR:-fallback} in the sibling tools treats it the same way.
local function env_or(name, fallback)
local value = os.getenv(name)
if value == nil or value == "" then
return fallback
end
return value
end
return {
home = home,
config_home = env_or("XDG_CONFIG_HOME", home .. "/.config"),
state_home = env_or("XDG_STATE_HOME", home .. "/.local/state"),
blob_path = env_or("BLOB_PATH", home .. "/.local/share/blob"),
}
-39
View File
@@ -1,39 +0,0 @@
-- Require every *.lua file in a directory in sorted order.
-- Used for Blob extension-style folders such as default/hypr/apps,
-- default/hypr/bindings, and ~/.local/state/blob/toggles/hypr.
-- Pass a module prefix for normal package.path modules, e.g.
-- require_all.files(paths.blob_path .. "/default/hypr/apps", "default.hypr.apps")
-- Pass nil as the prefix when the directory itself has been added to package.path.
-- Pass options.exclude as a set of base names (without ".lua") to skip; a legacy
-- file that must never be loaded as code stays on disk for a migration to remove.
local M = {}
local function shell_quote(path)
return "'" .. path:gsub("'", "'\\''") .. "'"
end
function M.files(dir, module_prefix, options)
local exclude = options and options.exclude or {}
local handle = io.popen("find " .. shell_quote(dir) .. " -maxdepth 1 -type f -name '*.lua' -printf '%f\\n' 2>/dev/null | sort")
if handle then
for filename in handle:lines() do
local name = filename:gsub("%.lua$", "")
if not exclude[name] then
local module = name
if module_prefix then
module = module_prefix .. "." .. module
end
if options and options.reload then
package.loaded[module] = nil
end
require(module)
end
end
handle:close()
end
end
return M
-12
View File
@@ -1,12 +0,0 @@
-- Require a module only when it can be found on package.path.
-- Errors inside existing modules still surface normally.
local M = {}
function M.module(module)
if package.searchpath(module, package.path) then
return require(module)
end
end
return M
-23
View File
@@ -1,23 +0,0 @@
local paths = require("default.hypr.paths")
local require_all = require("default.hypr.require_all")
local toggles_dir = paths.state_home .. "/blob/toggles/hypr"
package.path = toggles_dir .. "/?.lua;" .. package.path
-- touchpad-disabled.lua / touchscreen-disabled.lua were generated Lua in older
-- versions and could carry an injected USB device name. They must never be loaded
-- as code again: exclude them so a not-yet-migrated install cannot execute a
-- leftover payload on reload. The migration recovers the name and deletes them.
require_all.files(toggles_dir, nil, {
reload = true,
exclude = {
["touchpad-disabled"] = true,
["touchscreen-disabled"] = true,
},
})
local disabled_input_device = require("default.hypr.disabled-input-device")
disabled_input_device("touchpad")
disabled_input_device("touchscreen")
require("default.hypr.workspace-layouts")
-2
View File
@@ -1,2 +0,0 @@
-- This directory is intended for permanent config toggle flags.
-- Do not remove this file; the directory always needs at least one file.
@@ -1,6 +0,0 @@
-- Avoid overly wide single-window layouts on wide screens.
hl.config({
layout = {
single_window_aspect_ratio = { 1, 1 },
},
})
-12
View File
@@ -1,12 +0,0 @@
-- Remove all window gaps and borders.
hl.config({
general = {
gaps_out = 0,
gaps_in = 0,
border_size = 0,
},
decoration = {
rounding = 0,
},
})
-25
View File
@@ -1,25 +0,0 @@
-- See https://wiki.hypr.land/Configuring/Basics/Window-Rules/
o.window(".*", { suppress_event = "maximize" })
-- Tag all windows for default opacity (apps can override with -default-opacity tag).
o.window(".*", { tag = "+default-opacity" })
-- Fix some dragging issues with XWayland.
o.window(
{
class = "^$",
title = "^$",
xwayland = true,
float = true,
fullscreen = false,
pin = false,
},
{ no_focus = true }
)
-- App-specific tweaks (may remove default-opacity tag).
require("default.hypr.apps")
-- Apply default opacity after apps have had a chance to opt out.
o.window({ tag = "default-opacity" }, { opacity = "0.985 0.96" })
-8
View File
@@ -1,8 +0,0 @@
-- Restore workspace layouts saved by blob-hypr-workspace-layout-toggle.
local paths = require("default.hypr.paths")
local require_all = require("default.hypr.require_all")
local layouts_dir = paths.state_home .. "/blob/workspace-layouts"
require_all.files(layouts_dir, "blob.workspace-layouts", { reload = true })