Compare commits
38 Commits
1559da170e
...
features/f
| Author | SHA1 | Date | |
|---|---|---|---|
| 98f9a77b23 | |||
| bc242ae956 | |||
| 4237b2ed9e | |||
| 7b0cf9934b | |||
| 9df5731ee1 | |||
| 3806935316 | |||
| 516f8eed82 | |||
| 369179c057 | |||
| 322804577f | |||
| 0eee14858b | |||
| 461f4c3fff | |||
| 2f9dfda410 | |||
| e35ceb1450 | |||
| 3d0a18866f | |||
| 6c415d6fbb | |||
| c4b4a3bd26 | |||
| 866ee0bb9b | |||
| e77c08a0a2 | |||
| 491509e864 | |||
| c4604f8e0f | |||
| 30d5f4c926 | |||
| 90d7042995 | |||
| b6714cf20b | |||
| 8877835ae0 | |||
| 32dd10c83d | |||
| f3df5e4f3d | |||
| 74953850fb | |||
| 04e50bad3f | |||
| a218026568 | |||
| 7c557bffc8 | |||
| 2eb122960d | |||
| 8c0347ee35 | |||
| 2935fc7cfe | |||
| 6f9651ccb3 | |||
| c88a96406a | |||
| 13c4f95e08 | |||
| c4f220d95a | |||
| f6d69332db |
22
README.md
22
README.md
@@ -26,6 +26,26 @@ My dotfiles, a constantly evolving set of configurations which I arguably spend
|
||||
too much time tweaking, but they make the command line feel like home, so here
|
||||
we are.
|
||||
|
||||
## 🖥️ Installation
|
||||
|
||||
In order to apply these dotfiles, you must have the SSH Key stored in your password manager,
|
||||
then connect the SSH-Agent to your shell. Once that is done, you will need the application
|
||||
**Stow** in order to keep it synced.
|
||||
|
||||
After this, you will need to git clone the repo in the Projects directory
|
||||
|
||||
```console
|
||||
mkdir Projects
|
||||
git clone ssh://git@gitea.thomasleewilliams.co.uk:2222/thomas/dotfiles.git
|
||||
```
|
||||
|
||||
Once that is complete, run this command:
|
||||
```console
|
||||
stow -d ~/Projects/dotfiles/ -t ~/ dotfiles
|
||||
```
|
||||
|
||||
This will sync wallpapers/config files to where they are usually located
|
||||
|
||||
## 📦 Programs
|
||||
|
||||
List of programs and tools I use.
|
||||
@@ -39,7 +59,7 @@ List of programs and tools I use.
|
||||
|📁 File Manager | [thunar](https://wiki.archlinux.org/title/Thunar) |
|
||||
|📝 Editor | [vs code](https://github.com/microsoft/vscode) / [neovim (changing to soon)](https://github.com/neovim/neovim) |
|
||||
|🌐 Browser | [zen](https://github.com/zen-browser/desktop) / [firefox](https://github.com/topics/firefox-browser) / [startpage](https://blog-bucket-personal.s3.eu-west-2.amazonaws.com/Screenshot_02-May_08-44-23_6614.png) |
|
||||
|📊 Bar | [waybar](https://github.com/Alexays/Waybar) |
|
||||
|📊 Bar | [waybar](https://github.com/Alexays/Waybar) / [quickshell](https://quickshell.org/) |
|
||||
|🚀 Launcher | [rofi](https://github.com/davatorium/rofi) |
|
||||
|🔒 Lockscreen | [hyprlock](https://github.com/hyprwm/hyprlock) |
|
||||
|🚪 Login Menu | [sddm](https://github.com/sddm/sddm) |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
file:///home/thomas/Projects
|
||||
file:///home/thomas/Documents
|
||||
file:///home/thomas/Downloads
|
||||
file:///home/thomas/Pictures
|
||||
|
||||
34
dotfiles/.config/hypr/appearance/animations.lua
Normal file
34
dotfiles/.config/hypr/appearance/animations.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Ported from UserConfigs/UserAnimations.conf (the active preset)
|
||||
--
|
||||
-- The 14 alternative presets from animations/*.conf were also ported, flat, as
|
||||
-- anim-*.lua (see the plan) — none of them are wired in. They now live in ../presets/.
|
||||
-- To switch styles, replace the body of this file with `require("presets.anim-<preset>")`,
|
||||
-- e.g. require("presets.anim-ml4w-classic").
|
||||
|
||||
hl.config({
|
||||
animations = {
|
||||
enabled = true,
|
||||
},
|
||||
})
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("smoothOut", { type = "bezier", points = { { 0.5, 0 }, { 0.99, 0.99 } } })
|
||||
hl.curve("smoothIn", { type = "bezier", points = { { 0.5, -0.5 }, { 0.68, 1.5 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 5, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "smoothOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 100, bezier = "liner", style = "loop" }) -- used by rainbow borders and rotating colors; speed clamped from 180 -> Lua hl.animation caps speed at 100
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 3, bezier = "smoothOut" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "overshot" })
|
||||
|
||||
-- animations for -git or version >0.42.0
|
||||
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 5, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
26
dotfiles/.config/hypr/appearance/colors.lua
Normal file
26
dotfiles/.config/hypr/appearance/colors.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- HyprFlux static color palette
|
||||
-- Ported from colours/hyprflux-colors.conf
|
||||
|
||||
local M = {}
|
||||
|
||||
M.background = "rgb(010102)"
|
||||
M.foreground = "rgb(FDF8FE)"
|
||||
M.color0 = "rgb(313131)"
|
||||
M.color1 = "rgb(09050C)"
|
||||
M.color2 = "rgb(221647)"
|
||||
M.color3 = "rgb(2C1A40)"
|
||||
M.color4 = "rgb(5E3887)"
|
||||
M.color5 = "rgb(7344A6)"
|
||||
M.color6 = "rgb(BAB0BD)"
|
||||
M.color7 = "rgb(F3ECF5)"
|
||||
M.color8 = "rgb(AAA5AC)"
|
||||
M.color9 = "rgb(0B0711)"
|
||||
M.color10 = "rgb(2D1D5F)"
|
||||
M.color11 = "rgb(3B2355)"
|
||||
M.color12 = "rgb(7D4AB4)"
|
||||
M.color13 = "rgb(9A5BDD)"
|
||||
M.color14 = "rgb(F8EAFC)"
|
||||
M.color15 = "rgb(F3ECF5)"
|
||||
|
||||
return M
|
||||
65
dotfiles/.config/hypr/appearance/decorations.lua
Normal file
65
dotfiles/.config/hypr/appearance/decorations.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Decoration Settings
|
||||
-- Ported from UserConfigs/UserDecorations.conf
|
||||
--
|
||||
-- Hyprland Wiki Links
|
||||
-- Animation - https://wiki.hyprland.org/Configuring/Animations/
|
||||
-- Decoration - https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
|
||||
local colors = require("appearance.colors")
|
||||
|
||||
hl.config({
|
||||
general = {
|
||||
border_size = 2,
|
||||
gaps_in = 2,
|
||||
gaps_out = 4,
|
||||
|
||||
col = {
|
||||
active_border = colors.color12,
|
||||
inactive_border = colors.color10,
|
||||
},
|
||||
},
|
||||
|
||||
decoration = {
|
||||
rounding = 10,
|
||||
|
||||
active_opacity = 1.0,
|
||||
inactive_opacity = 0.9,
|
||||
fullscreen_opacity = 1.0,
|
||||
|
||||
dim_inactive = true,
|
||||
dim_strength = 0.1,
|
||||
dim_special = 0.8,
|
||||
|
||||
shadow = {
|
||||
enabled = false,
|
||||
range = 3,
|
||||
render_power = 1,
|
||||
|
||||
color = colors.color12,
|
||||
color_inactive = colors.color10,
|
||||
},
|
||||
|
||||
blur = {
|
||||
enabled = true,
|
||||
size = 6,
|
||||
passes = 2,
|
||||
ignore_opacity = true,
|
||||
new_optimizations = true,
|
||||
special = true,
|
||||
popups = true,
|
||||
},
|
||||
},
|
||||
|
||||
group = {
|
||||
col = {
|
||||
border_active = colors.color15,
|
||||
},
|
||||
|
||||
groupbar = {
|
||||
col = {
|
||||
active = colors.color0,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
87
dotfiles/.config/hypr/core/env.lua
Normal file
87
dotfiles/.config/hypr/core/env.lua
Normal file
@@ -0,0 +1,87 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Environment variables. See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Environment-variables/
|
||||
-- Ported from UserConfigs/ENVariables.conf
|
||||
|
||||
-- Toolkit Backend Variables
|
||||
hl.env("GDK_BACKEND", "wayland,x11,*")
|
||||
hl.env("QT_QPA_PLATFORM", "wayland;xcb")
|
||||
hl.env("CLUTTER_BACKEND", "wayland")
|
||||
|
||||
-- Run SDL2 applications on Wayland.
|
||||
-- Remove or set to x11 if games that provide older versions of SDL cause compatibility issues
|
||||
-- hl.env("SDL_VIDEODRIVER", "wayland")
|
||||
|
||||
-- xdg Specifications
|
||||
hl.env("XDG_CURRENT_DESKTOP", "Hyprland")
|
||||
hl.env("XDG_SESSION_DESKTOP", "Hyprland")
|
||||
hl.env("XDG_SESSION_TYPE", "wayland")
|
||||
|
||||
-- QT Variables
|
||||
hl.env("QT_AUTO_SCREEN_SCALE_FACTOR", "1")
|
||||
hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
|
||||
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct")
|
||||
hl.env("QT_QPA_PLATFORMTHEME", "qt6ct")
|
||||
|
||||
-- hyprland-qt-support
|
||||
hl.env("QT_QUICK_CONTROLS_STYLE", "org.hyprland.style")
|
||||
|
||||
-- xwayland apps scale fix (useful if you use monitor scaling).
|
||||
-- Set same value if you use scaling in monitors.lua
|
||||
-- 1 is 100% 1.5 is 150%
|
||||
-- see https://wiki.hyprland.org/Configuring/XWayland/
|
||||
hl.env("GDK_SCALE", "1")
|
||||
hl.env("QT_SCALE_FACTOR", "1")
|
||||
|
||||
-- Bibata-Modern-Ice-Cursor
|
||||
-- NOTE! You must have the hyprcursor version to activate this.
|
||||
-- https://wiki.hyprland.org/Hypr-Ecosystem/hyprcursor/
|
||||
hl.env("HYPRCURSOR_THEME", "Bibata-Modern-Classic")
|
||||
hl.env("HYPRCURSOR_SIZE", "24")
|
||||
|
||||
-- firefox
|
||||
hl.env("MOZ_ENABLE_WAYLAND", "1")
|
||||
|
||||
-- electron >28 apps (may help) ##
|
||||
-- https://www.electronjs.org/docs/latest/api/environment-variables
|
||||
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "auto") -- auto selects Wayland if possible, X11 otherwise
|
||||
|
||||
-- NVIDIA
|
||||
-- This is from the Hyprland wiki. Uncomment below if an nvidia GPU is detected.
|
||||
-- See hyprland wiki https://wiki.hyprland.org/Nvidia/#environment-variables
|
||||
|
||||
-- hl.env("LIBVA_DRIVER_NAME", "nvidia")
|
||||
-- hl.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia")
|
||||
-- hl.env("NVD_BACKEND", "direct")
|
||||
-- hl.env("GSK_RENDERER", "ngl")
|
||||
|
||||
-- additional ENV's for nvidia. Caution, activate with care
|
||||
-- hl.env("GBM_BACKEND", "nvidia-drm")
|
||||
|
||||
-- hl.env("__GL_GSYNC_ALLOWED", "1") -- adaptive Vsync
|
||||
-- hl.env("__NV_PRIME_RENDER_OFFLOAD", "1")
|
||||
-- hl.env("__VK_LAYER_NV_optimus", "NVIDIA_only")
|
||||
-- hl.env("WLR_DRM_NO_ATOMIC", "1")
|
||||
|
||||
-- FOR VM and POSSIBLY NVIDIA
|
||||
-- LIBGL_ALWAYS_SOFTWARE software mesa rendering
|
||||
-- hl.env("LIBGL_ALWAYS_SOFTWARE", "1") -- Warning. May cause hyprland to crash
|
||||
-- hl.env("WLR_RENDERER_ALLOW_SOFTWARE", "1")
|
||||
|
||||
-- nvidia firefox (for hardware acceleration on FF)?
|
||||
-- check this post https://github.com/elFarto/nvidia-vaapi-driver#configuration
|
||||
-- hl.env("MOZ_DISABLE_RDD_SANDBOX", "1")
|
||||
-- hl.env("EGL_PLATFORM", "wayland")
|
||||
|
||||
---- Aquamarine Environment Variables ---- ( Hyprland > 0.45 )
|
||||
-- https://wiki.hyprland.org/Configuring/Environment-variables/#aquamarine-environment-variables
|
||||
-- hl.env("AQ_TRACE", "1") -- Enables more verbose logging.
|
||||
-- hl.env("AQ_DRM_DEVICES", "/dev/dri/card1:/dev/dri/card0") -- explicit list of DRM devices (GPUs) to use.
|
||||
-- hl.env("AQ_MGPU_NO_EXPLICIT", "1") -- Disables explicit syncing on mgpu buffers
|
||||
-- hl.env("AQ_NO_MODIFIERS", "1") -- Disables modifiers for DRM buffers
|
||||
|
||||
---- Hyprland Environment Variables ----
|
||||
-- https://wiki.hyprland.org/Configuring/Environment-variables/#hyprland-environment-variables
|
||||
-- hl.env("HYPRLAND_TRACE", "1") -- Enables more verbose logging.
|
||||
-- hl.env("HYPRLAND_NO_RT", "1") -- Disables realtime priority setting by Hyprland.
|
||||
-- hl.env("HYPRLAND_NO_SD_NOTIFY", "1") -- If systemd, disables the 'sd_notify' calls.
|
||||
-- hl.env("HYPRLAND_NO_SD_VARS", "1") -- Disables management of variables in systemd and dbus activation environments.
|
||||
29
dotfiles/.config/hypr/core/hyprmod.lua
Normal file
29
dotfiles/.config/hypr/core/hyprmod.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
-- Generated by HyprMod
|
||||
-- Ported from hyprland-gui.conf
|
||||
--
|
||||
-- HyprMod isn't currently installed on this machine, so this is a straight syntax port of
|
||||
-- whatever it last wrote. If you reinstall HyprMod, prefer letting its own "Migrate to Lua…"
|
||||
-- wizard take over managing this file instead of hand-editing it going forward.
|
||||
|
||||
hl.config({
|
||||
general = {
|
||||
allow_tearing = true,
|
||||
snap = {
|
||||
enabled = false,
|
||||
},
|
||||
},
|
||||
misc = {
|
||||
vrr = 1,
|
||||
},
|
||||
})
|
||||
|
||||
-- Monitors
|
||||
hl.monitor({ output = "DP-2", mode = "1920x1080@165.00Hz", position = "0x0", scale = 1, bitdepth = 10, cm = "wide" })
|
||||
|
||||
-- Keybinds
|
||||
-- (unbind = SUPER SHIFT, Print / unbind = SUPER, up from the source .conf have no Lua equivalent needed —
|
||||
-- we just don't define a conflicting bind elsewhere; SUPER + up -> movefocus u below is otherwise identical
|
||||
-- to the one already in keybinds.lua, kept here too for parity with the original file)
|
||||
hl.bind("SUPER + Print", hl.dsp.exec_cmd("hyprshot -o " .. os.getenv("HOME") .. "/Pictures/Screenshots-Linux -m region"))
|
||||
hl.bind("SUPER + up", hl.dsp.focus({ direction = "u" }))
|
||||
hl.bind("SUPER + SHIFT + Print", hl.dsp.exec_cmd("hyprshot -o " .. os.getenv("HOME") .. "/Pictures/Screenshots-Linux -m window"))
|
||||
120
dotfiles/.config/hypr/core/settings.lua
Normal file
120
dotfiles/.config/hypr/core/settings.lua
Normal file
@@ -0,0 +1,120 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- User Settings
|
||||
-- Ported from UserConfigs/UserSettings.conf
|
||||
--
|
||||
-- refer to Hyprland wiki for more info https://wiki.hyprland.org/Configuring/Variables/
|
||||
-- NOTE: some settings are in animations.lua / decorations.lua
|
||||
|
||||
hl.config({
|
||||
dwindle = {
|
||||
preserve_split = true,
|
||||
-- smart_split = true,
|
||||
special_scale_factor = 1,
|
||||
},
|
||||
|
||||
master = {
|
||||
new_status = "master",
|
||||
new_on_top = true,
|
||||
mfact = 0.5,
|
||||
},
|
||||
|
||||
general = {
|
||||
resize_on_border = true,
|
||||
layout = "dwindle",
|
||||
},
|
||||
|
||||
input = {
|
||||
kb_layout = "gb",
|
||||
kb_variant = "",
|
||||
kb_model = "",
|
||||
kb_options = "",
|
||||
kb_rules = "",
|
||||
repeat_rate = 50,
|
||||
repeat_delay = 300,
|
||||
sensitivity = 0.6, -- mouse sensitivity
|
||||
-- accel_profile = "", -- flat or adaptive or blank or EMPTY means libinput's default mode
|
||||
numlock_by_default = true,
|
||||
left_handed = false,
|
||||
follow_mouse = 1,
|
||||
float_switch_override_focus = false,
|
||||
|
||||
touchpad = {
|
||||
disable_while_typing = true,
|
||||
natural_scroll = true,
|
||||
clickfinger_behavior = false,
|
||||
middle_button_emulation = false,
|
||||
tap_to_click = true,
|
||||
drag_lock = false,
|
||||
},
|
||||
|
||||
-- below for devices with touchdevice ie. touchscreen
|
||||
touchdevice = {
|
||||
enabled = true,
|
||||
},
|
||||
|
||||
-- below is for tablet, see link above for proper variables
|
||||
tablet = {
|
||||
transform = 0,
|
||||
left_handed = false,
|
||||
},
|
||||
},
|
||||
|
||||
-- "gestures" here only holds the tuning knobs. workspace_swipe/workspace_swipe_fingers
|
||||
-- were removed from this category upstream in favor of hl.gesture() (see below) — and
|
||||
-- were already commented out in the source .conf, so the swipe gesture itself stays
|
||||
-- disabled here too, ported as-is.
|
||||
gestures = {
|
||||
workspace_swipe_distance = 500,
|
||||
workspace_swipe_invert = true,
|
||||
workspace_swipe_min_speed_to_force = 30,
|
||||
workspace_swipe_cancel_ratio = 0.5,
|
||||
workspace_swipe_create_new = true,
|
||||
workspace_swipe_forever = true,
|
||||
-- workspace_swipe_use_r = true, -- uncomment if wanted a forever create a new workspace with swipe right
|
||||
},
|
||||
|
||||
misc = {
|
||||
disable_hyprland_logo = true,
|
||||
disable_splash_rendering = true,
|
||||
vrr = 2,
|
||||
mouse_move_enables_dpms = true,
|
||||
enable_swallow = false,
|
||||
swallow_regex = "^(kitty)$",
|
||||
focus_on_activate = false,
|
||||
initial_workspace_tracking = 0,
|
||||
middle_click_paste = false,
|
||||
enable_anr_dialog = true, -- Application not Responding (ANR)
|
||||
anr_missed_pings = 15, -- ANR Threshold default 1 is too low
|
||||
},
|
||||
|
||||
-- opengl = {
|
||||
-- nvidia_anti_flicker = true,
|
||||
-- },
|
||||
|
||||
binds = {
|
||||
workspace_back_and_forth = true,
|
||||
allow_workspace_cycles = true,
|
||||
pass_mouse_when_bound = false,
|
||||
},
|
||||
|
||||
-- Could help when scaling and not pixelating
|
||||
xwayland = {
|
||||
enabled = true,
|
||||
force_zero_scaling = true,
|
||||
},
|
||||
|
||||
render = {
|
||||
direct_scanout = false,
|
||||
},
|
||||
|
||||
cursor = {
|
||||
sync_gsettings_theme = true,
|
||||
no_hardware_cursors = 2, -- change to 1 if you want to disable
|
||||
enable_hyprcursor = true,
|
||||
warp_on_change_workspace = 2,
|
||||
no_warps = true,
|
||||
},
|
||||
})
|
||||
|
||||
-- Uncomment to actually enable the touchpad workspace-swipe gesture (see the note above):
|
||||
-- hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
|
||||
52
dotfiles/.config/hypr/core/startup.lua
Normal file
52
dotfiles/.config/hypr/core/startup.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Commands and Apps to be executed at launch
|
||||
-- Ported from UserConfigs/Startup_Apps.conf
|
||||
-- See https://wiki.hypr.land/Configuring/Basics/Autostart/
|
||||
|
||||
local scriptsDir = os.getenv("HOME") .. "/.config/hypr/scripts"
|
||||
|
||||
hl.on("hyprland.start", function()
|
||||
-- wallpaper stuff
|
||||
hl.exec_cmd("awww-daemon --format xrgb")
|
||||
-- hl.exec_cmd("mpvpaper '*' -o \"load-scripts=no no-audio --loop\" $livewallpaper")
|
||||
|
||||
-- Startup
|
||||
hl.exec_cmd("dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP")
|
||||
hl.exec_cmd("systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP")
|
||||
|
||||
-- Polkit (Polkit Gnome / KDE)
|
||||
hl.exec_cmd(scriptsDir .. "/Polkit.sh")
|
||||
|
||||
-- startup apps
|
||||
hl.exec_cmd("nm-applet --indicator")
|
||||
hl.exec_cmd("swaync")
|
||||
-- hl.exec_cmd("ags")
|
||||
-- hl.exec_cmd("blueman-applet")
|
||||
-- hl.exec_cmd("rog-control-center")
|
||||
hl.exec_cmd("waybar")
|
||||
hl.exec_cmd("qs") -- quickshell AGS Desktop Overview alternative
|
||||
|
||||
-- clipboard manager
|
||||
hl.exec_cmd("wl-paste --type text --watch cliphist store")
|
||||
hl.exec_cmd("wl-paste --type image --watch cliphist store")
|
||||
|
||||
-- Starting hypridle to start hyprlock
|
||||
hl.exec_cmd("hypridle")
|
||||
|
||||
-- Float + center the Bitwarden extension pop-out (window rules can't catch it — see the script)
|
||||
hl.exec_cmd(scriptsDir .. "/FloatBitwardenPopup.sh")
|
||||
|
||||
hl.dispatch(hl.dsp.focus({ workspace = 3 }))
|
||||
|
||||
-- NOTE: the trailing "&&" here is a stray leftover from the original .conf (dangling, does nothing) — ported as-is
|
||||
hl.exec_cmd("/usr/bin/Plexamp.AppImage &&")
|
||||
hl.exec_cmd("sleep 3 && vesktop")
|
||||
hl.dispatch(hl.dsp.focus({ workspace = 1 }))
|
||||
hl.exec_cmd("bitwarden-desktop --ozone-platform-hint=auto --silent")
|
||||
|
||||
-- Here are a list of features available but disabled by default
|
||||
-- hl.exec_cmd("awww-daemon --format xrgb && awww img " .. os.getenv("HOME") .. "/Pictures/wallpapers/mecha-nostalgia.png") -- persistent wallpaper
|
||||
|
||||
-- xdg-desktop-portal-hyprland (should be auto starting. However, you can force to start)
|
||||
-- hl.exec_cmd(scriptsDir .. "/PortalHyprland.sh")
|
||||
end)
|
||||
@@ -10,6 +10,6 @@ monitor = DP-2, 1920x1080@165.00Hz, 0x0, 1, bitdepth, 10, cm, wide
|
||||
# Keybinds
|
||||
unbind = SUPER SHIFT, Print
|
||||
unbind = SUPER, up
|
||||
bind = SUPER, Print, exec, hyprshot -o $HOME/Pictures/Screenshots -m region
|
||||
bind = SUPER, Print, exec, hyprshot -o $HOME/Pictures/Screenshots-Linux -m region
|
||||
bind = SUPER, up, movefocus, u
|
||||
bind = SUPER SHIFT, Print, exec, hyprshot -o $HOME/Pictures/Screenshots -m window
|
||||
bind = SUPER SHIFT, Print, exec, hyprshot -o $HOME/Pictures/Screenshots-Linux -m window
|
||||
|
||||
42
dotfiles/.config/hypr/hyprland.lua
Normal file
42
dotfiles/.config/hypr/hyprland.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Ported from hyprland.conf — see https://wiki.hyprland.org/
|
||||
--
|
||||
-- This mirrors the old source= chain 1:1, just as require()'d Lua modules instead
|
||||
-- of hyprlang includes. See each file for its origin. The presets/ preset library
|
||||
-- (anim-*.lua) is also ported but intentionally not required from here — only the
|
||||
-- currently-active style (appearance/animations.lua) is wired in, exactly like before.
|
||||
--
|
||||
-- Modules live in subfolders sorted by concern (core/, appearance/, layout/, input/,
|
||||
-- presets/) — require() paths below use dotted module names, which Lua resolves to
|
||||
-- the matching subfolder (e.g. require("core.env") -> core/env.lua).
|
||||
|
||||
-- Initial boot script: applies initial wallpapers, theming, new settings etc.
|
||||
-- suggest not changing/deleting this, including the reference file at ~/.config/hypr/.initial_startup_done
|
||||
-- as long as that reference file is present, initial-boot.sh will not execute
|
||||
hl.on("hyprland.start", function()
|
||||
hl.exec_cmd(os.getenv("HOME") .. "/.config/hypr/initial-boot.sh")
|
||||
end)
|
||||
|
||||
-- ## This is where you want to start tinkering
|
||||
require("core.startup") -- put your start-up packages in core/startup.lua
|
||||
require("core.env") -- Environment variables to load
|
||||
require("layout.windowrules") -- all about Hyprland Window Rules and Layer Rules
|
||||
require("appearance.decorations") -- Decorations config
|
||||
require("appearance.animations") -- Animation config
|
||||
require("input.keybinds") -- Put your own keybinds here
|
||||
require("core.settings") -- Main Hyprland Settings
|
||||
|
||||
-- nwg-displays / hyprdynamicmonitors
|
||||
require("layout.monitors")
|
||||
require("layout.workspaces")
|
||||
|
||||
-- NOTE: the original hyprland.conf used a plain `exec` (not `exec-once`) here, so it
|
||||
-- re-ran on every `hyprctl reload`, not just at startup. hl.on("hyprland.start", ...)
|
||||
-- only fires once at actual compositor start — close enough for a one-shot gsettings
|
||||
-- call, but worth knowing if you relied on the reload-time re-apply.
|
||||
hl.on("hyprland.start", function()
|
||||
hl.exec_cmd("gsettings set org.gnome.desktop.interface cursor-theme Future-black Cursors")
|
||||
end)
|
||||
|
||||
-- HyprMod managed settings
|
||||
require("core.hyprmod")
|
||||
176
dotfiles/.config/hypr/input/keybinds.lua
Normal file
176
dotfiles/.config/hypr/input/keybinds.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Ported from UserConfigs/Keybinds.conf
|
||||
-- See https://wiki.hypr.land/Configuring/Basics/Binds/ and .../Basics/Dispatchers/
|
||||
|
||||
local mainMod = "SUPER"
|
||||
local scriptsDir = os.getenv("HOME") .. "/.config/hypr/scripts"
|
||||
local userScripts = os.getenv("HOME") .. "/.config/hypr/UserScripts"
|
||||
|
||||
-- settings for user default apps
|
||||
local edit = os.getenv("EDITOR") or "nvim"
|
||||
|
||||
-- These two are for UserKeybinds.conf & Waybar Modules
|
||||
local term = "foot" -- Terminal
|
||||
local files = "thunar" -- File Manager
|
||||
|
||||
-- Default Search Engine for ROFI Search (SUPER S)
|
||||
local searchEngine = "https://www.google.com/search?q={}"
|
||||
|
||||
-- common shortcuts
|
||||
hl.bind(mainMod .. " + SPACE", hl.dsp.exec_cmd(".config/rofi/launchers/type-4/launcher.sh")) -- desktop overview (if installed)
|
||||
hl.bind(mainMod .. " + Q", hl.dsp.window.close()) -- close active (not kill)
|
||||
hl.bind(mainMod .. " + SHIFT + Q", hl.dsp.exec_cmd(scriptsDir .. "/KillActiveProcess.sh")) -- Kill active process
|
||||
hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(term)) -- terminal
|
||||
hl.bind(mainMod .. " + F", hl.dsp.exec_cmd(files)) -- file manager
|
||||
hl.bind(mainMod .. " + B", hl.dsp.exec_cmd("zen-browser")) -- Launch Zen Browser New Everytime
|
||||
hl.bind(mainMod .. " + V", hl.dsp.exec_cmd(scriptsDir .. "/ClipManager.sh")) -- Clipboard Manager
|
||||
hl.bind(mainMod .. " + C", hl.dsp.exec_cmd("code --ozone-platform=x11")) -- Visual Studio Code
|
||||
hl.bind(mainMod .. " + O", hl.dsp.exec_cmd("obsidian --ozone-platform=x11")) -- Obsidian
|
||||
hl.bind(mainMod .. " + X", hl.dsp.exec_cmd("vesktop")) -- Vesktop(Discord)
|
||||
hl.bind(mainMod .. " + M", hl.dsp.exec_cmd("fdm")) -- Free Download Manager
|
||||
hl.bind(mainMod .. " + W", hl.dsp.exec_cmd("waypaper")) -- Wayland Wallpaper Setter
|
||||
hl.bind(mainMod .. " + L", hl.dsp.exec_cmd("hyprlock")) -- Screen Locker
|
||||
hl.bind(mainMod .. " + DELETE", hl.dsp.exec_cmd("~/.config/rofi/powermenu/type-2/powermenu.sh")) -- Power Menu
|
||||
hl.bind(mainMod .. " + S", hl.dsp.exec_cmd("cd /home/thomas/hyprmod && uv run hyprmod")) -- Hyprmod - A Hyprland settings manager (if installed)
|
||||
hl.bind(mainMod .. " + SHIFT + R", hl.dsp.exec_cmd(scriptsDir .. "/Refresh.sh")) -- Refresh waybar, swaync, rofirrr
|
||||
hl.bind(mainMod .. " + N", hl.dsp.exec_cmd("swaync-client -t -sw")) -- swayNC notification panel
|
||||
|
||||
-- FEATURES / EXTRAS
|
||||
hl.bind(mainMod .. " + SHIFT + F", hl.dsp.window.fullscreen({ mode = "fullscreen" })) -- whole full screen
|
||||
hl.bind(mainMod .. " + SHIFT + SPACE", hl.dsp.window.float({ action = "toggle" })) -- Float Mode
|
||||
|
||||
-- Desktop zooming or magnifier
|
||||
hl.bind(mainMod .. " + ALT + mouse_down", hl.dsp.exec_cmd([[hyprctl keyword cursor:zoom_factor "$(hyprctl getoption cursor:zoom_factor | awk 'NR==1 {factor = $2; if (factor < 1) {factor = 1}; print factor * 2.0}')"]]))
|
||||
hl.bind(mainMod .. " + ALT + mouse_up", hl.dsp.exec_cmd([[hyprctl keyword cursor:zoom_factor "$(hyprctl getoption cursor:zoom_factor | awk 'NR==1 {factor = $2; if (factor < 1) {factor = 1}; print factor / 2.0}')"]]))
|
||||
|
||||
-- Waybar / Bar related
|
||||
hl.bind(mainMod .. " + CTRL + ALT + B", hl.dsp.exec_cmd("pkill -SIGUSR1 waybar")) -- Toggle hide/show waybar
|
||||
|
||||
-- FEATURES / EXTRAS (UserScripts)
|
||||
hl.bind(mainMod .. " + SHIFT + A", hl.dsp.exec_cmd(scriptsDir .. "/Animations.sh")) -- hyprland animations menu
|
||||
hl.bind(mainMod .. " + SHIFT + Z", hl.dsp.exec_cmd(userScripts .. "/ZshChangeTheme.sh")) -- Change oh-my-zsh theme
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
-- Default Keybinds
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
hl.bind("CTRL + ALT + Delete", hl.dsp.exit()) -- exit Hyprland
|
||||
|
||||
-- Master Layout
|
||||
hl.bind(mainMod .. " + CTRL + D", hl.dsp.layout("removemaster"))
|
||||
hl.bind(mainMod .. " + I", hl.dsp.layout("addmaster"))
|
||||
hl.bind(mainMod .. " + J", hl.dsp.layout("cyclenext"))
|
||||
hl.bind(mainMod .. " + K", hl.dsp.layout("cycleprev"))
|
||||
hl.bind(mainMod .. " + CTRL + Return", hl.dsp.layout("swapwithmaster"))
|
||||
|
||||
-- Dwindle Layout
|
||||
hl.bind(mainMod .. " + P", hl.dsp.window.pseudo()) -- dwindle
|
||||
|
||||
-- Special Keys / Hot Keys
|
||||
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd(scriptsDir .. "/Volume.sh --inc"), { repeating = true, locked = true }) -- volume up
|
||||
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd(scriptsDir .. "/Volume.sh --dec"), { repeating = true, locked = true }) -- volume down
|
||||
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd(scriptsDir .. "/Volume.sh --toggle-mic"), { locked = true }) -- mic mute
|
||||
hl.bind("XF86AudioMute", hl.dsp.exec_cmd(scriptsDir .. "/Volume.sh --toggle"), { locked = true }) -- mute
|
||||
hl.bind("XF86Sleep", hl.dsp.exec_cmd("systemctl suspend"), { locked = true }) -- sleep button
|
||||
hl.bind("XF86RFKill", hl.dsp.exec_cmd(scriptsDir .. "/AirplaneMode.sh"), { locked = true }) -- Airplane mode
|
||||
|
||||
-- media controls using keyboards
|
||||
hl.bind("XF86MediaPlayPause", hl.dsp.exec_cmd(scriptsDir .. "/MediaCtrl.sh --pause"), { locked = true })
|
||||
hl.bind("XF86AudioPause", hl.dsp.exec_cmd(scriptsDir .. "/MediaCtrl.sh --pause"), { locked = true })
|
||||
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd(scriptsDir .. "/MediaCtrl.sh --pause"), { locked = true })
|
||||
hl.bind("XF86AudioNext", hl.dsp.exec_cmd(scriptsDir .. "/MediaCtrl.sh --nxt"), { locked = true })
|
||||
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd(scriptsDir .. "/MediaCtrl.sh --prv"), { locked = true })
|
||||
hl.bind("XF86AudioStop", hl.dsp.exec_cmd(scriptsDir .. "/MediaCtrl.sh --stop"), { locked = true })
|
||||
|
||||
-- Resize windows
|
||||
hl.bind(mainMod .. " + SHIFT + left", hl.dsp.window.resize({ x = -50, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind(mainMod .. " + SHIFT + right", hl.dsp.window.resize({ x = 50, y = 0, relative = true }), { repeating = true })
|
||||
hl.bind(mainMod .. " + SHIFT + up", hl.dsp.window.resize({ x = 0, y = -50, relative = true }), { repeating = true })
|
||||
hl.bind(mainMod .. " + SHIFT + down", hl.dsp.window.resize({ x = 0, y = 50, relative = true }), { repeating = true })
|
||||
|
||||
-- Move windows
|
||||
hl.bind(mainMod .. " + CTRL + left", hl.dsp.window.move({ direction = "l" }))
|
||||
hl.bind(mainMod .. " + CTRL + right", hl.dsp.window.move({ direction = "r" }))
|
||||
hl.bind(mainMod .. " + CTRL + up", hl.dsp.window.move({ direction = "u" }))
|
||||
hl.bind(mainMod .. " + CTRL + down", hl.dsp.window.move({ direction = "d" }))
|
||||
|
||||
-- Swap windows
|
||||
hl.bind(mainMod .. " + ALT + left", hl.dsp.window.swap({ direction = "l" }))
|
||||
hl.bind(mainMod .. " + ALT + right", hl.dsp.window.swap({ direction = "r" }))
|
||||
hl.bind(mainMod .. " + ALT + up", hl.dsp.window.swap({ direction = "u" }))
|
||||
hl.bind(mainMod .. " + ALT + down", hl.dsp.window.swap({ direction = "d" }))
|
||||
|
||||
-- Move focus with mainMod + arrow keys
|
||||
hl.bind(mainMod .. " + left", hl.dsp.focus({ direction = "l" }))
|
||||
hl.bind(mainMod .. " + right", hl.dsp.focus({ direction = "r" }))
|
||||
hl.bind(mainMod .. " + up", hl.dsp.focus({ direction = "u" }))
|
||||
hl.bind(mainMod .. " + down", hl.dsp.focus({ direction = "d" }))
|
||||
|
||||
-- Workspaces related
|
||||
hl.bind(mainMod .. " + tab", hl.dsp.focus({ workspace = "m+1" }))
|
||||
hl.bind(mainMod .. " + SHIFT + tab", hl.dsp.focus({ workspace = "m-1" }))
|
||||
|
||||
-- Special workspace
|
||||
-- hl.bind(mainMod .. " + SHIFT + U", hl.dsp.window.move({ workspace = "special" }))
|
||||
-- hl.bind(mainMod .. " + U", hl.dsp.workspace.toggle_special())
|
||||
hl.bind(mainMod .. " + U", hl.dsp.workspace.toggle_special("nyx"))
|
||||
hl.bind(mainMod .. " + SHIFT + U", hl.dsp.window.move({ workspace = "special:nyx" }))
|
||||
|
||||
-- The following mappings use key codes to better support various keyboard layouts
|
||||
-- 1 is code:10, 2 is code:11, etc
|
||||
-- Switch workspaces with mainMod + [0-9]
|
||||
for i, code in ipairs({ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }) do
|
||||
hl.bind(mainMod .. " + code:" .. code, hl.dsp.focus({ workspace = i })) -- code:10 = key 1 ... code:19 = key 0
|
||||
end
|
||||
|
||||
-- Move active window and follow to workspace mainMod + SHIFT + [0-9]
|
||||
for i, code in ipairs({ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }) do
|
||||
hl.bind(mainMod .. " + SHIFT + code:" .. code, hl.dsp.window.move({ workspace = i, follow = true }))
|
||||
end
|
||||
hl.bind(mainMod .. " + SHIFT + bracketleft", hl.dsp.window.move({ workspace = -1, follow = true })) -- brackets [
|
||||
hl.bind(mainMod .. " + SHIFT + bracketright", hl.dsp.window.move({ workspace = 1, follow = true })) -- brackets ]
|
||||
|
||||
-- Move active window to a workspace silently mainMod + CTRL + [0-9]
|
||||
for i, code in ipairs({ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }) do
|
||||
hl.bind(mainMod .. " + CTRL + code:" .. code, hl.dsp.window.move({ workspace = i, follow = false }))
|
||||
end
|
||||
hl.bind(mainMod .. " + CTRL + bracketleft", hl.dsp.window.move({ workspace = -1, follow = false })) -- brackets [
|
||||
hl.bind(mainMod .. " + CTRL + bracketright", hl.dsp.window.move({ workspace = 1, follow = false })) -- brackets ]
|
||||
|
||||
-- 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" }))
|
||||
hl.bind(mainMod .. " + period", hl.dsp.focus({ workspace = "e+1" }))
|
||||
hl.bind(mainMod .. " + comma", 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 }) -- mouse:272 = left click
|
||||
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) -- mouse:273 = right click
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
-- Laptop specific keybinds
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
-- for disabling Touchpad. hyprctl devices to get device name.
|
||||
local touchpadDevice = "asue1209:00-04f3:319f-touchpad"
|
||||
|
||||
hl.bind("XF86KbdBrightnessDown", hl.dsp.exec_cmd(scriptsDir .. "/BrightnessKbd.sh --dec"), { repeating = true }) -- decrease keyboard brightness
|
||||
hl.bind("XF86KbdBrightnessUp", hl.dsp.exec_cmd(scriptsDir .. "/BrightnessKbd.sh --inc"), { repeating = true }) -- increase keyboard brightness
|
||||
hl.bind("XF86Launch1", hl.dsp.exec_cmd("rog-control-center")) -- ASUS Armory crate button
|
||||
hl.bind("XF86Launch3", hl.dsp.exec_cmd("asusctl led-mode -n")) -- FN+F4 Switch keyboard RGB profile
|
||||
hl.bind("XF86Launch4", hl.dsp.exec_cmd("asusctl profile -n")) -- FN+F5 change of fan profiles (Quiet, Balance, Performance)
|
||||
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd(scriptsDir .. "/Brightness.sh --dec"), { repeating = true }) -- decrease monitor brightness
|
||||
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd(scriptsDir .. "/Brightness.sh --inc"), { repeating = true }) -- increase monitor brightness
|
||||
hl.bind("XF86TouchpadToggle", hl.dsp.exec_cmd(scriptsDir .. "/TouchPad.sh")) -- disable touchpad
|
||||
|
||||
-- Screenshot keybindings using F6 (no PrintSrc button)
|
||||
-- hl.bind(mainMod .. " + F6", hl.dsp.exec_cmd(scriptsDir .. "/ScreenShot.sh --now")) -- screenshot
|
||||
-- hl.bind(mainMod .. " + SHIFT + F6", hl.dsp.exec_cmd(scriptsDir .. "/ScreenShot.sh --area")) -- screenshot (area)
|
||||
-- hl.bind(mainMod .. " + CTRL + F6", hl.dsp.exec_cmd(scriptsDir .. "/ScreenShot.sh --in5")) -- screenshot (5 secs delay)
|
||||
-- hl.bind(mainMod .. " + ALT + F6", hl.dsp.exec_cmd(scriptsDir .. "/ScreenShot.sh --in10")) -- screenshot (10 secs delay)
|
||||
-- hl.bind("ALT + F6", hl.dsp.exec_cmd(scriptsDir .. "/ScreenShot.sh --active")) -- screenshot (active window only)
|
||||
|
||||
hl.device({
|
||||
name = touchpadDevice,
|
||||
enabled = true,
|
||||
})
|
||||
14
dotfiles/.config/hypr/layout/monitors.lua
Normal file
14
dotfiles/.config/hypr/layout/monitors.lua
Normal file
@@ -0,0 +1,14 @@
|
||||
-- Generated with hyprdynamicmonitors freeze.
|
||||
-- This is a template that you can edit, it is just a starter that pulled your current monitor setup,
|
||||
-- adjust as needed.
|
||||
-- You can use templates here etc, see https://github.com/fiffeek/hyprdynamicmonitors/blob/main/examples/basic/hyprconfigs/template.go.tmpl.
|
||||
-- Monitors are given arbitrary tags (the "monitor" prefix and the ID coming from hyprland).
|
||||
-- If you are using the TUI to edit, leave this at the end of the file (the last monitor config applies)
|
||||
-- and leave the markers.
|
||||
-- Ported from UserConfigs/monitors.conf — hyprdynamicmonitors already supports writing this file
|
||||
-- directly as monitors.lua once hyprland.lua is active, so re-running it should stay compatible.
|
||||
-- <<<<< TUI AUTO START
|
||||
hl.monitor({ output = "desc:LG Electronics LG ULTRAWIDE 0x0002E68A", mode = "2560x1080@60.00000", position = "1920x-740", scale = 1.00000000, transform = 3, vrr = 0 })
|
||||
hl.monitor({ output = "desc:HP Inc. HP 524pf VNG447017D", mode = "1920x1080@100.00000", position = "-1920x0", scale = 1.00000000, transform = 0, vrr = 0 })
|
||||
hl.monitor({ output = "desc:Acer Technologies VG240Y S 0x126191DE", mode = "1920x1080@165.00301", position = "0x0", scale = 1.00000000, transform = 0, vrr = 1, bitdepth = 10, cm = "wide" })
|
||||
-- <<<<< TUI AUTO END
|
||||
203
dotfiles/.config/hypr/layout/windowrules.lua
Normal file
203
dotfiles/.config/hypr/layout/windowrules.lua
Normal file
@@ -0,0 +1,203 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- For window rules and layer rules
|
||||
-- Ported from UserConfigs/WindowRules.conf
|
||||
-- See https://wiki.hypr.land/Configuring/Basics/Window-Rules/
|
||||
|
||||
-- windowrule - tags - add apps under appropriate tag to use the same settings
|
||||
-- browser tags
|
||||
hl.window_rule({ name = "windowrule-1", tag = "+browser", match = { class = "^([Ff]irefox|org.mozilla.firefox|[Ff]irefox-esr|[Ff]irefox-bin)$" } })
|
||||
hl.window_rule({ name = "rustdesk", tag = "+rustdesk", match = { class = "^(rustdesk)$", xwayland = true } })
|
||||
hl.window_rule({ name = "windowrule-2", tag = "+browser", match = { class = "^([Gg]oogle-chrome(-beta|-dev|-unstable)?)$" } })
|
||||
hl.window_rule({ name = "windowrule-3", tag = "+browser", match = { class = "^(chrome-.+-Default)$" } }) -- Chrome PWAs
|
||||
-- hl.window_rule({ name = "browser-chromium", tag = "+browser", match = { class = "^([Cc]hromium)$" } })
|
||||
hl.window_rule({ name = "windowrule-4", tag = "+browser", match = { class = "^([Mm]icrosoft-edge(-stable|-beta|-dev|-unstable))$" } })
|
||||
hl.window_rule({ name = "windowrule-5", tag = "+browser", match = { class = "^(Brave-browser(-beta|-dev|-unstable)?)$" } })
|
||||
hl.window_rule({ name = "windowrule-6", tag = "+browser", match = { class = "^([Tt]horium-browser|[Cc]achy-browser)$" } })
|
||||
hl.window_rule({ name = "windowrule-7", tag = "+browser", match = { class = "^(zen-alpha|zen)$" } })
|
||||
|
||||
-- notif tags
|
||||
hl.window_rule({ name = "windowrule-8", tag = "+notif", match = { class = "^(swaync-control-center|swaync-notification-window|swaync-client|class)$" } })
|
||||
|
||||
-- HyprFlux settings tag
|
||||
hl.window_rule({ name = "windowrule-9", tag = "+HyprFlux_Cheat", match = { title = "^(HyprFlux Quick Cheat Sheet)$" } })
|
||||
hl.window_rule({ name = "windowrule-10", tag = "+HyprFlux_Settings", match = { title = "^(HyprFlux Settings)$" } })
|
||||
hl.window_rule({ name = "windowrule-11", tag = "+HyprFlux-Settings", match = { class = "^(nwg-displays|nwg-look)$" } })
|
||||
|
||||
-- terminal tags
|
||||
hl.window_rule({ name = "windowrule-12", tag = "+terminal", match = { class = "^(Alacritty|kitty|kitty-dropterm)$" } })
|
||||
|
||||
-- email tags
|
||||
hl.window_rule({ name = "windowrule-13", tag = "+email", match = { class = "^([Tt]hunderbird|org.gnome.Evolution)$" } })
|
||||
hl.window_rule({ name = "windowrule-14", tag = "+email", match = { class = "^(eu.betterbird.Betterbird)$" } })
|
||||
|
||||
-- project tags
|
||||
hl.window_rule({ name = "windowrule-15", tag = "+projects", match = { class = "^(codium|codium-url-handler|VSCodium)$" } })
|
||||
hl.window_rule({ name = "windowrule-16", tag = "+projects", match = { class = "^(VSCode|code-url-handler)$" } })
|
||||
hl.window_rule({ name = "windowrule-17", tag = "+projects", match = { class = "^(jetbrains-.+)$" } }) -- JetBrains IDEs
|
||||
|
||||
-- screenshare tags
|
||||
hl.window_rule({ name = "windowrule-18", tag = "+screenshare", match = { class = "^(com.obsproject.Studio)$" } })
|
||||
|
||||
-- IM tags
|
||||
-- hl.window_rule({ tag = "+im", match = { class = "^([Dd]iscord|[Ww]ebCord|[Vv]esktop)$" } })
|
||||
hl.window_rule({
|
||||
name = "windowrule-19",
|
||||
tag = "+im",
|
||||
center = true,
|
||||
float = true,
|
||||
size = "monitor_w*0.6 monitor_h*0.7",
|
||||
match = { class = "^([Ff]erdium)$" },
|
||||
})
|
||||
hl.window_rule({ name = "windowrule-20", tag = "+im", match = { class = "^([Ww]hatsapp-for-linux)$" } })
|
||||
hl.window_rule({ name = "windowrule-21", tag = "+im", match = { class = "^(ZapZap|com.rtosta.zapzap)$" } })
|
||||
hl.window_rule({ name = "windowrule-22", tag = "+im", match = { class = "^(org.telegram.desktop|io.github.tdesktop_x64.TDesktop)$" } })
|
||||
hl.window_rule({ name = "windowrule-23", tag = "+im", match = { class = "^(teams-for-linux)$" } })
|
||||
hl.window_rule({ name = "windowrule-24", tag = "+im", match = { class = "^(im.riot.Riot|Element)$" } }) -- Element Matrix client
|
||||
|
||||
-- game tags
|
||||
hl.window_rule({ name = "windowrule-25", tag = "+games", match = { class = "^(gamescope)$" } })
|
||||
hl.window_rule({ name = "windowrule-26", tag = "+games", match = { class = "^(steam_app_\\d+)$" } })
|
||||
|
||||
-- gamestore tags
|
||||
hl.window_rule({ name = "windowrule-27", tag = "+gamestore", match = { class = "^([Ss]team)$" } })
|
||||
hl.window_rule({ name = "windowrule-28", tag = "+gamestore", match = { title = "^([Ll]utris)$" } })
|
||||
hl.window_rule({ name = "windowrule-29", tag = "+gamestore", match = { class = "^(com.heroicgameslauncher.hgl)$" } })
|
||||
|
||||
-- file-manager tags
|
||||
hl.window_rule({ name = "windowrule-30", tag = "+file-manager", match = { class = "^([Tt]hunar|org.gnome.Nautilus|[Pp]cmanfm-qt)$" } })
|
||||
hl.window_rule({ name = "windowrule-31", tag = "+file-manager", match = { class = "^(app.drey.Warp)$" } })
|
||||
|
||||
-- wallpaper tags
|
||||
hl.window_rule({ name = "windowrule-32", tag = "+wallpaper", match = { class = "^([Ww]aytrogen)$" } })
|
||||
|
||||
-- multimedia tags
|
||||
hl.window_rule({ name = "windowrule-33", tag = "+multimedia", match = { class = "^([Aa]udacious)$" } })
|
||||
|
||||
-- multimedia-video tags
|
||||
hl.window_rule({ name = "windowrule-34", tag = "+multimedia_video", match = { class = "^([Mm]pv|vlc)$" } })
|
||||
|
||||
-- settings tags
|
||||
hl.window_rule({ name = "windowrule-35", tag = "+settings", center = true, match = { title = "^(ROG Control)$" } })
|
||||
hl.window_rule({ name = "windowrule-36", tag = "+settings", match = { class = "^(wihotspot(-gui)?)$" } }) -- wifi hotspot
|
||||
hl.window_rule({ name = "windowrule-37", tag = "+settings", match = { class = "^([Bb]aobab|org.gnome.[Bb]aobab)$" } }) -- Disk usage analyzer
|
||||
hl.window_rule({ name = "windowrule-38", tag = "+settings", match = { class = "^(gnome-disks|wihotspot(-gui)?)$" } })
|
||||
hl.window_rule({ name = "windowrule-39", tag = "+settings", match = { title = "(Kvantum Manager)" } })
|
||||
hl.window_rule({ name = "windowrule-40", tag = "+settings", match = { class = "^(file-roller|org.gnome.FileRoller)$" } }) -- archive manager
|
||||
hl.window_rule({ name = "windowrule-41", tag = "+settings", match = { class = "^(nm-applet|nm-connection-editor|blueman-manager)$" } })
|
||||
hl.window_rule({ name = "windowrule-42", tag = "+settings", center = true, match = { class = "^(pavucontrol|org.pulseaudio.pavucontrol|com.saivert.pwvucontrol)$" } })
|
||||
hl.window_rule({ name = "windowrule-43", tag = "+settings", match = { class = "^(qt5ct|qt6ct|[Yy]ad)$" } })
|
||||
hl.window_rule({ name = "windowrule-44", tag = "+settings", match = { class = "(xdg-desktop-portal-gtk)" } })
|
||||
hl.window_rule({ name = "windowrule-45", tag = "+settings", match = { class = "^(org.kde.polkit-kde-authentication-agent-1)$" } })
|
||||
hl.window_rule({ name = "windowrule-46", tag = "+settings", match = { class = "^([Rr]ofi)$" } })
|
||||
|
||||
-- Custom rule
|
||||
hl.window_rule({ name = "windowrule-47", tile = true, match = { class = "^([Cc]hromium)$" } })
|
||||
hl.window_rule({ name = "windowrule-48", size = "monitor_w*0.25 monitor_h*0.55", float = true, match = { class = "^(com.network.manager)$" } })
|
||||
hl.window_rule({ name = "windowrule-49", workspace = "special:nyx", match = { class = "^([Vv]esktop|[Dd]iscord|[Ss]potify)$" } })
|
||||
hl.window_rule({ name = "windowrule-255", size = "monitor_w*0.75 monitor_h*0.80", float = true, match = { class = "^([Bb]itwarden)$" } })
|
||||
|
||||
-- viewer tags
|
||||
hl.window_rule({ name = "windowrule-50", tag = "+viewer", match = { class = "^(gnome-system-monitor|org.gnome.SystemMonitor|io.missioncenter.MissionCenter)$" } }) -- system monitor
|
||||
hl.window_rule({ name = "windowrule-51", tag = "+viewer", match = { class = "^(evince)$" } }) -- document viewer
|
||||
hl.window_rule({ name = "windowrule-52", tag = "+viewer", match = { class = "^(eog|org.gnome.Loupe)$" } }) -- image viewer
|
||||
|
||||
-- Some special override rules
|
||||
hl.window_rule({ name = "windowrule-53", no_blur = true, opacity = "1.0", match = { tag = "multimedia_video*" } })
|
||||
|
||||
-- POSITION
|
||||
-- hl.window_rule({ center = true, match = { float = true } }) -- warning, it cause even the menu to float and center.
|
||||
hl.window_rule({ name = "windowrule-54", center = true, float = true, size = "monitor_w*0.65 monitor_h*0.9", match = { tag = "HyprFlux_Cheat*" } })
|
||||
hl.window_rule({ name = "windowrule-55", center = true, float = true, match = { class = "([Tt]hunar)", title = "negative:(.*[Tt]hunar.*)" } })
|
||||
hl.window_rule({ name = "windowrule-56", center = true, float = true, match = { tag = "HyprFlux-Settings*" } })
|
||||
hl.window_rule({ name = "windowrule-57", center = true, match = { title = "^(Keybindings)$" } })
|
||||
hl.window_rule({ name = "windowrule-58", center = true, size = "monitor_w*0.6 monitor_h*0.7", match = { class = "^([Ww]hatsapp-for-linux|ZapZap|com.rtosta.zapzap)$" } })
|
||||
hl.window_rule({
|
||||
name = "windowrule-59",
|
||||
move = "monitor_w*0.61 monitor_h*0.07",
|
||||
float = true,
|
||||
size = "monitor_w*0.38 monitor_h*0.38",
|
||||
pin = true,
|
||||
keep_aspect_ratio = true,
|
||||
match = { title = "^(Picture-in-Picture)$" },
|
||||
})
|
||||
|
||||
-- hl.window_rule({ move = "72% 7%", match = { title = "^(Firefox)$" } })
|
||||
|
||||
-- windowrule to avoid idle for fullscreen apps
|
||||
-- hl.window_rule({ idle_inhibit = "fullscreen", match = { class = "^(*)$" } })
|
||||
-- hl.window_rule({ idle_inhibit = "fullscreen", match = { title = "^(*)$" } })
|
||||
hl.window_rule({ name = "windowrule-60", idle_inhibit = "fullscreen", match = { fullscreen = true } })
|
||||
|
||||
-- Discord to workspace 3
|
||||
-- NOTE: these two used the old single-line windowrule syntax in the source .conf (match packed
|
||||
-- oddly with the rule); windowrule-49 above already sends the same apps to special:nyx, so this
|
||||
-- looks like a pre-existing conflict in the original config rather than something introduced here —
|
||||
-- ported literally, last-defined-wins as before.
|
||||
hl.window_rule({ workspace = "3", match = { class = "^([Vv]esktop|[Dd]iscord|[Ss]potify|[Pp]lexamp)$" } })
|
||||
hl.window_rule({ size = "20% 20%", match = { class = "^([Pp]lexamp)" } })
|
||||
|
||||
-- FLOAT
|
||||
hl.window_rule({ name = "windowrule-75", float = true, opacity = "0.9 0.7", size = "monitor_w*0.7 monitor_h*0.7", match = { tag = "wallpaper*" } })
|
||||
hl.window_rule({ name = "windowrule-76", float = true, opacity = "0.8 0.7", size = "monitor_w*0.7 monitor_h*0.7", match = { tag = "settings*" } })
|
||||
hl.window_rule({ name = "windowrule-77", float = true, match = { tag = "viewer*" } })
|
||||
hl.window_rule({ name = "windowrule-78", float = true, match = { class = "([Zz]oom|onedriver|onedriver-launcher)$" } })
|
||||
hl.window_rule({ name = "windowrule-79", float = true, match = { class = "(org.gnome.Calculator)", title = "(Calculator)" } })
|
||||
hl.window_rule({ name = "windowrule-80", float = true, match = { class = "^(mpv|com.github.rafostar.Clapper)$" } })
|
||||
hl.window_rule({ name = "windowrule-81", float = true, match = { class = "^([Qq]alculate-gtk)$" } })
|
||||
-- hl.window_rule({ float = true, match = { class = "^([Ww]hatsapp-for-linux|ZapZap|com.rtosta.zapzap)$" } })
|
||||
-- hl.window_rule({ float = true, match = { title = "^(Firefox)$" } })
|
||||
hl.window_rule({ name = "windowrule-82", float = true, match = { class = "^(fdm|freedownloadmanager)$" } })
|
||||
hl.window_rule({ name = "windowrule-83", float = true, match = { class = "^(proton-authenticator)$" } })
|
||||
hl.window_rule({ name = "windowrule-84", float = true, match = { class = "^([Ww]indscribe)$" } })
|
||||
|
||||
-- windowrule - ######### float popups and dialogue #######
|
||||
hl.window_rule({ name = "windowrule-85", float = true, center = true, match = { title = "^(Authentication Required)$" } })
|
||||
hl.window_rule({ name = "windowrule-86", float = true, match = { class = "(codium|codium-url-handler|VSCodium)", title = "negative:(.*codium.*|.*VSCodium.*)" } })
|
||||
hl.window_rule({ name = "windowrule-87", float = true, match = { class = "^(com.heroicgameslauncher.hgl)$", title = "negative:(Heroic Games Launcher)" } })
|
||||
hl.window_rule({ name = "windowrule-88", float = true, match = { class = "^([Ss]team)$", title = "negative:^([Ss]team)$" } })
|
||||
hl.window_rule({ name = "windowrule-89", float = true, size = "monitor_w*0.7 monitor_h*0.6", center = true, match = { title = "^(Add Folder to Workspace)$" } })
|
||||
hl.window_rule({ name = "windowrule-90", float = true, size = "monitor_w*0.7 monitor_h*0.6", center = true, match = { title = "^(Save As)$" } })
|
||||
hl.window_rule({ name = "windowrule-91", float = true, size = "monitor_w*0.7 monitor_h*0.6", match = { initial_title = "(Open Files)" } })
|
||||
hl.window_rule({ name = "windowrule-92", float = true, center = true, size = "monitor_w*0.16 monitor_h*0.12", match = { title = "^(SDDM Background)$" } }) -- HyprFlux YAD for setting SDDM background
|
||||
-- END of float popups and dialogue #######
|
||||
|
||||
-- OPACITY
|
||||
-- hl.window_rule({ opacity = "0.9 0.7", match = { tag = "browser*" } })
|
||||
-- hl.window_rule({ opacity = "0.9 0.8", match = { tag = "projects*" } })
|
||||
-- hl.window_rule({ opacity = "0.94 0.86", match = { tag = "im*" } })
|
||||
-- hl.window_rule({ opacity = "0.94 0.86", match = { tag = "multimedia*" } })
|
||||
-- hl.window_rule({ opacity = "0.9 0.8", match = { tag = "file-manager*" } })
|
||||
hl.window_rule({ name = "windowrule-93", opacity = "0.8 0.7", match = { tag = "terminal*" } })
|
||||
-- hl.window_rule({ opacity = "0.82 0.75", match = { tag = "viewer*" } })
|
||||
hl.window_rule({ name = "windowrule-94", opacity = "0.8 0.7", match = { class = "^(gedit|org.gnome.TextEditor|mousepad)$" } })
|
||||
hl.window_rule({ name = "windowrule-95", opacity = "0.9 0.8", match = { class = "^(deluge)$" } })
|
||||
hl.window_rule({ name = "windowrule-96", opacity = "0.9 0.8", match = { class = "^(seahorse)$" } }) -- gnome-keyring gui
|
||||
-- hl.window_rule({ opacity = "0.95 0.75", match = { title = "^(Picture-in-Picture)$" } })
|
||||
|
||||
-- SIZE
|
||||
-- hl.window_rule({ size = "25% 25%", match = { title = "^(Firefox)$" } })
|
||||
|
||||
-- PINNING
|
||||
-- hl.window_rule({ pin = true, match = { title = "^(Firefox)$" } })
|
||||
|
||||
-- windowrule - extras
|
||||
|
||||
-- BLUR & FULLSCREEN
|
||||
-- hl.window_rule({ active_border_color = "rgb(EE4B55)", inactive_border_color = "rgb(880808)", match = { fullscreen = true } })
|
||||
-- hl.window_rule({ active_border_color = "rgb(282737)", inactive_border_color = "rgb(1E1D2D)", match = { float = true } })
|
||||
-- hl.window_rule({ opacity = "0.8 0.8", match = { pin = true } })
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
-- LAYER RULES
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
hl.layer_rule({ name = "layerrule-1", blur = true, ignore_alpha = 0, match = { namespace = "rofi" } })
|
||||
hl.layer_rule({ name = "layerrule-2", blur = true, ignore_alpha = 0, match = { namespace = "notifications" } })
|
||||
hl.layer_rule({ name = "layerrule-3", blur = true, ignore_alpha = 0.5, match = { namespace = "quickshell:overview" } })
|
||||
|
||||
-- hl.layer_rule({ ignore_alpha = 0.5, match = { tag = "notif*" } })
|
||||
-- hl.layer_rule({ no_anim = true (unset), match = { class = "^([Rr]ofi)$" } })
|
||||
-- hl.layer_rule({ blur = true, match = { class = "^([Rr]ofi)$" } })
|
||||
-- hl.layer_rule({ ignore_alpha = 0, match = { class = "<rofi>" } })
|
||||
-- hl.layer_rule({ ignore_alpha = 0, match = { namespace = "overview" } })
|
||||
-- hl.layer_rule({ blur = true, match = { namespace = "overview" } })
|
||||
29
dotfiles/.config/hypr/layout/workspaces.lua
Normal file
29
dotfiles/.config/hypr/layout/workspaces.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- NOTE: This will be overwritten by NWG-Displays
|
||||
-- once you use it and click apply.
|
||||
-- (nwg-displays already supports writing this file directly as workspaces.lua once
|
||||
-- hyprland.lua is active, so re-running it should stay compatible.)
|
||||
--
|
||||
-- Ported from UserConfigs/workspaces.conf
|
||||
-- You can set workspace rules to achieve workspace-specific behaviors.
|
||||
-- For instance, you can define a workspace where all windows are drawn without borders or gaps.
|
||||
-- https://wiki.hypr.land/Configuring/Basics/Workspace-Rules/
|
||||
|
||||
-- Assigning workspace to a certain monitor. Below are just examples
|
||||
hl.workspace_rule({ workspace = 1, monitor = "DP-2", default = true })
|
||||
hl.workspace_rule({ workspace = 2, monitor = "DP-1", default = true })
|
||||
hl.workspace_rule({ workspace = 3, monitor = "HDMI-A-1", default = true })
|
||||
-- hl.workspace_rule({ workspace = 4, monitor = "eDP-1" })
|
||||
-- hl.workspace_rule({ workspace = 5, monitor = "DP-2" })
|
||||
-- hl.workspace_rule({ workspace = 6, monitor = "DP-2" })
|
||||
-- hl.workspace_rule({ workspace = 7, monitor = "DP-2" })
|
||||
-- hl.workspace_rule({ workspace = 8, monitor = "DP-2" })
|
||||
|
||||
-- example rules (from wiki)
|
||||
-- hl.workspace_rule({ workspace = 3, rounding = false, decorate = false })
|
||||
-- hl.workspace_rule({ workspace = "name:coding", rounding = false, decorate = false, gaps_in = 0, gaps_out = 0, border = false, monitor = "DP-1" })
|
||||
-- hl.workspace_rule({ workspace = 8, border_size = 8 })
|
||||
-- hl.workspace_rule({ workspace = "name:Hello", monitor = "DP-1", default = true })
|
||||
-- hl.workspace_rule({ workspace = "name:gaming", monitor = "desc:Chimei Innolux Corporation 0x150C", default = true })
|
||||
-- hl.workspace_rule({ workspace = 5, on_created_empty = "[float] firefox" })
|
||||
-- hl.workspace_rule({ workspace = "special:scratchpad", on_created_empty = "foot" })
|
||||
22
dotfiles/.config/hypr/presets/anim-00-default.lua
Normal file
22
dotfiles/.config/hypr/presets/anim-00-default.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Ported from animations/00-default.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 5, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "smoothOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 100, bezier = "liner", style = "loop" }) -- used by rainbow borders and rotating colors; speed clamped from 180 -> Lua hl.animation caps speed at 100
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 3, bezier = "smoothOut" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "overshot" })
|
||||
|
||||
-- animations for -git or version >0.42.0
|
||||
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 5, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
25
dotfiles/.config/hypr/presets/anim-01-default-v2.lua
Normal file
25
dotfiles/.config/hypr/presets/anim-01-default-v2.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- old animations
|
||||
-- Ported from animations/01-default - v2.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("myBezier", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("linear", { type = "bezier", points = { { 0.0, 0.0 }, { 1.0, 1.0 } } })
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("slow", { type = "bezier", points = { { 0, 0.85 }, { 0.3, 1 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.7, 0.6 }, { 0.1, 1.1 } } })
|
||||
hl.curve("bounce", { type = "bezier", points = { { 1.1, 1.6 }, { 0.1, 0.85 } } })
|
||||
hl.curve("sligshot", { type = "bezier", points = { { 1, -1 }, { 0.15, 1.25 } } })
|
||||
hl.curve("nice", { type = "bezier", points = { { 0, 6.9 }, { 0.5, -4.20 } } })
|
||||
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 5, bezier = "slow", style = "popin" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "winOut", style = "popin" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "linear" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 100, bezier = "linear", style = "loop" }) -- used by rainbow borders and rotating colors; speed clamped from 180 -> Lua hl.animation caps speed at 100
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 5, bezier = "overshot" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "wind" })
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 5, bezier = "bounce", style = "popin" })
|
||||
4
dotfiles/.config/hypr/presets/anim-03-disable.lua
Normal file
4
dotfiles/.config/hypr/presets/anim-03-disable.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- Ported from animations/03- Disable Animation.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = false } })
|
||||
39
dotfiles/.config/hypr/presets/anim-end-4.lua
Normal file
39
dotfiles/.config/hypr/presets/anim-end-4.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "End-4"
|
||||
-- credit https://github.com/end-4/dots-hyprland
|
||||
-- Ported from animations/END-4.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
-- Animation curves
|
||||
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
|
||||
hl.curve("md3_standard", { type = "bezier", points = { { 0.2, 0 }, { 0, 1 } } })
|
||||
hl.curve("md3_decel", { type = "bezier", points = { { 0.05, 0.7 }, { 0.1, 1 } } })
|
||||
hl.curve("md3_accel", { type = "bezier", points = { { 0.3, 0 }, { 0.8, 0.15 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.1 } } })
|
||||
hl.curve("crazyshot", { type = "bezier", points = { { 0.1, 1.5 }, { 0.76, 0.92 } } })
|
||||
hl.curve("hyprnostretch", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.0 } } })
|
||||
hl.curve("menu_decel", { type = "bezier", points = { { 0.1, 1 }, { 0, 1 } } })
|
||||
hl.curve("menu_accel", { type = "bezier", points = { { 0.38, 0.04 }, { 1, 0.07 } } })
|
||||
hl.curve("easeInOutCirc", { type = "bezier", points = { { 0.85, 0 }, { 0.15, 1 } } })
|
||||
hl.curve("easeOutCirc", { type = "bezier", points = { { 0, 0.55 }, { 0.45, 1 } } })
|
||||
hl.curve("easeOutExpo", { type = "bezier", points = { { 0.16, 1 }, { 0.3, 1 } } })
|
||||
hl.curve("softAcDecel", { type = "bezier", points = { { 0.26, 0.26 }, { 0.15, 1 } } })
|
||||
hl.curve("md2", { type = "bezier", points = { { 0.4, 0 }, { 0.2, 1 } } }) -- use with .2s duration
|
||||
|
||||
-- Animation configs
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 3, bezier = "md3_decel", style = "popin 60%" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 3, bezier = "md3_decel", style = "popin 60%" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "md3_accel", style = "popin 60%" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 3, bezier = "md3_decel" })
|
||||
-- hl.animation({ leaf = "layers", enabled = true, speed = 2, bezier = "md3_decel", style = "slide" })
|
||||
hl.animation({ leaf = "layersIn", enabled = true, speed = 3, bezier = "menu_decel", style = "slide" })
|
||||
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.6, bezier = "menu_accel" })
|
||||
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 2, bezier = "menu_decel" })
|
||||
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 4.5, bezier = "menu_accel" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 7, bezier = "menu_decel", style = "slide" })
|
||||
-- hl.animation({ leaf = "workspaces", enabled = true, speed = 2.5, bezier = "softAcDecel", style = "slide" })
|
||||
-- hl.animation({ leaf = "workspaces", enabled = true, speed = 7, bezier = "menu_decel", style = "slidefade 15%" })
|
||||
-- hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "md3_decel", style = "slidefadevert 15%" })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "md3_decel", style = "slidevert" })
|
||||
21
dotfiles/.config/hypr/presets/anim-hyde-default.lua
Normal file
21
dotfiles/.config/hypr/presets/anim-hyde-default.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Default"
|
||||
-- credit https://github.com/prasanthrangan/hyprdots
|
||||
-- Ported from animations/HYDE - default.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 6, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 30, bezier = "liner", style = "once" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "wind" })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 5, bezier = "wind", style = "slidevert" })
|
||||
20
dotfiles/.config/hypr/presets/anim-hyde-minimal-1.lua
Normal file
20
dotfiles/.config/hypr/presets/anim-hyde-minimal-1.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Minimal-1"
|
||||
-- credit https://github.com/prasanthrangan/hyprdots
|
||||
-- Ported from animations/HYDE - minimal-1.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 6, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 30, bezier = "liner", style = "once" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "wind" })
|
||||
14
dotfiles/.config/hypr/presets/anim-hyde-minimal-2.lua
Normal file
14
dotfiles/.config/hypr/presets/anim-hyde-minimal-2.lua
Normal file
@@ -0,0 +1,14 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Minimal-2"
|
||||
-- credit https://github.com/prasanthrangan/hyprdots
|
||||
-- Ported from animations/HYDE - minimal-2.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("quart", { type = "bezier", points = { { 0.25, 1 }, { 0.5, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "quart", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 6, bezier = "quart" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 6, bezier = "quart" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 6, bezier = "quart" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 6, bezier = "quart" })
|
||||
40
dotfiles/.config/hypr/presets/anim-hyde-optimized.lua
Normal file
40
dotfiles/.config/hypr/presets/anim-hyde-optimized.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Optimized"
|
||||
-- credit https://github.com/prasanthrangan/hyprdots
|
||||
-- Ported from animations/HYDE - optimized.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.85 }, { 0.03, 0.97 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.07, 0.88 }, { 0.04, 0.99 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.20, -0.15 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
hl.curve("md3_standard", { type = "bezier", points = { { 0.12, 0 }, { 0, 1 } } })
|
||||
hl.curve("md3_decel", { type = "bezier", points = { { 0.05, 0.80 }, { 0.10, 0.97 } } })
|
||||
hl.curve("md3_accel", { type = "bezier", points = { { 0.20, 0 }, { 0.80, 0.08 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.85 }, { 0.07, 1.04 } } })
|
||||
hl.curve("crazyshot", { type = "bezier", points = { { 0.1, 1.22 }, { 0.68, 0.98 } } })
|
||||
hl.curve("hyprnostretch", { type = "bezier", points = { { 0.05, 0.82 }, { 0.03, 0.94 } } })
|
||||
hl.curve("menu_decel", { type = "bezier", points = { { 0.05, 0.82 }, { 0, 1 } } })
|
||||
hl.curve("menu_accel", { type = "bezier", points = { { 0.20, 0 }, { 0.82, 0.10 } } })
|
||||
hl.curve("easeInOutCirc", { type = "bezier", points = { { 0.75, 0 }, { 0.15, 1 } } })
|
||||
hl.curve("easeOutCirc", { type = "bezier", points = { { 0, 0.48 }, { 0.38, 1 } } })
|
||||
hl.curve("easeOutExpo", { type = "bezier", points = { { 0.10, 0.94 }, { 0.23, 0.98 } } })
|
||||
hl.curve("softAcDecel", { type = "bezier", points = { { 0.20, 0.20 }, { 0.15, 1 } } })
|
||||
hl.curve("md2", { type = "bezier", points = { { 0.30, 0 }, { 0.15, 1 } } })
|
||||
|
||||
hl.curve("OutBack", { type = "bezier", points = { { 0.28, 1.40 }, { 0.58, 1 } } })
|
||||
hl.curve("easeInOutCirc", { type = "bezier", points = { { 0.78, 0 }, { 0.15, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1.6, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 82, bezier = "liner", style = "once" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 3.2, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 2.8, bezier = "easeOutCirc" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 3.0, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 1.8, bezier = "md3_decel" })
|
||||
hl.animation({ leaf = "layersIn", enabled = true, speed = 1.8, bezier = "menu_decel", style = "slide" })
|
||||
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "menu_accel" })
|
||||
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.6, bezier = "menu_decel" })
|
||||
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.8, bezier = "menu_accel" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 4.0, bezier = "menu_decel", style = "slide" })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 2.3, bezier = "md3_decel", style = "slidefadevert 15%" })
|
||||
32
dotfiles/.config/hypr/presets/anim-hyde-vertical.lua
Normal file
32
dotfiles/.config/hypr/presets/anim-hyde-vertical.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Vertical"
|
||||
-- credit https://github.com/prasanthrangan/hyprdots
|
||||
-- Ported from animations/HYDE - Vertical.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("fluent_decel", { type = "bezier", points = { { 0, 0.2 }, { 0.4, 1 } } })
|
||||
hl.curve("easeOutCirc", { type = "bezier", points = { { 0, 0.55 }, { 0.45, 1 } } })
|
||||
hl.curve("easeOutCubic", { type = "bezier", points = { { 0.33, 1 }, { 0.68, 1 } } })
|
||||
hl.curve("easeinoutsine", { type = "bezier", points = { { 0.37, 0 }, { 0.63, 1 } } })
|
||||
|
||||
-- Windows
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 1.5, bezier = "easeinoutsine", style = "popin 60%" }) -- window open
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.5, bezier = "easeOutCubic", style = "popin 60%" }) -- window close.
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 1.5, bezier = "easeinoutsine", style = "slide" }) -- everything in between, moving, dragging, resizing.
|
||||
|
||||
-- Fading
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 2.5, bezier = "fluent_decel" })
|
||||
|
||||
hl.animation({ leaf = "fadeLayersIn", enabled = false })
|
||||
hl.animation({ leaf = "border", enabled = false })
|
||||
|
||||
-- Layers
|
||||
hl.animation({ leaf = "layers", enabled = true, speed = 1.5, bezier = "easeinoutsine", style = "popin" })
|
||||
|
||||
-- Workspaces
|
||||
-- styles: slide, slidevert, fade, slidefade, slidefadevert
|
||||
-- hl.animation({ leaf = "workspaces", enabled = true, speed = 3, bezier = "fluent_decel", style = "slidefade 30%" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 3, bezier = "fluent_decel", style = "slidefadevert 30%" })
|
||||
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 2, bezier = "fluent_decel", style = "slidefade 10%" })
|
||||
52
dotfiles/.config/hypr/presets/anim-mahaveer-1.lua
Normal file
52
dotfiles/.config/hypr/presets/anim-mahaveer-1.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Me-1"
|
||||
-- credit https://github.com/mahaveergurjar
|
||||
-- Ported from animations/Mahaveer - me-1.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
-- Animation curves
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
-- hl.curve("linear", { type = "bezier", points = { {0,0}, {1,1} } })
|
||||
hl.curve("md3_standard", { type = "bezier", points = { { 0.2, 0 }, { 0, 1 } } })
|
||||
hl.curve("md3_decel", { type = "bezier", points = { { 0.05, 0.7 }, { 0.1, 1 } } })
|
||||
hl.curve("md3_accel", { type = "bezier", points = { { 0.3, 0 }, { 0.8, 0.15 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.1 } } })
|
||||
hl.curve("crazyshot", { type = "bezier", points = { { 0.1, 1.5 }, { 0.76, 0.92 } } })
|
||||
hl.curve("hyprnostretch", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.0 } } })
|
||||
hl.curve("menu_decel", { type = "bezier", points = { { 0.1, 1 }, { 0, 1 } } })
|
||||
hl.curve("menu_accel", { type = "bezier", points = { { 0.38, 0.04 }, { 1, 0.07 } } })
|
||||
hl.curve("easeInOutCirc", { type = "bezier", points = { { 0.85, 0 }, { 0.15, 1 } } })
|
||||
hl.curve("easeOutCirc", { type = "bezier", points = { { 0, 0.55 }, { 0.45, 1 } } })
|
||||
hl.curve("easeOutExpo", { type = "bezier", points = { { 0.16, 1 }, { 0.3, 1 } } })
|
||||
hl.curve("softAcDecel", { type = "bezier", points = { { 0.26, 0.26 }, { 0.15, 1 } } })
|
||||
hl.curve("md2", { type = "bezier", points = { { 0.4, 0 }, { 0.2, 1 } } }) -- use with .2s duration
|
||||
|
||||
-- Animation configs
|
||||
-- hl.animation({ leaf = "windows", enabled = true, speed = 3, bezier = "md3_decel", style = "popin 60%" })
|
||||
-- hl.animation({ leaf = "windowsIn", enabled = true, speed = 3, bezier = "md3_decel", style = "popin 60%" })
|
||||
-- hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "md3_accel", style = "popin 60%" })
|
||||
-- hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 30, bezier = "liner", style = "once" })
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 6, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
|
||||
-- hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 3, bezier = "md3_decel" })
|
||||
-- hl.animation({ leaf = "layers", enabled = true, speed = 2, bezier = "md3_decel", style = "slide" })
|
||||
hl.animation({ leaf = "layersIn", enabled = true, speed = 3, bezier = "menu_decel", style = "slide" })
|
||||
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.6, bezier = "menu_accel" })
|
||||
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 2, bezier = "menu_decel" })
|
||||
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 4.5, bezier = "menu_accel" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 7, bezier = "menu_decel", style = "slide" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "wind" })
|
||||
-- hl.animation({ leaf = "workspaces", enabled = true, speed = 2.5, bezier = "softAcDecel", style = "slide" })
|
||||
-- hl.animation({ leaf = "workspaces", enabled = true, speed = 7, bezier = "menu_decel", style = "slidefade 15%" })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "md3_decel", style = "slidefadevert 15%" })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "md3_decel", style = "slidevert" })
|
||||
27
dotfiles/.config/hypr/presets/anim-mahaveer-2.lua
Normal file
27
dotfiles/.config/hypr/presets/anim-mahaveer-2.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Me-2"
|
||||
-- credit https://github.com/mahaveergurjar
|
||||
-- Ported from animations/Mahaveer - me-2.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("smoothOut", { type = "bezier", points = { { 0.5, 0 }, { 0.99, 0.99 } } })
|
||||
hl.curve("smoothIn", { type = "bezier", points = { { 0.5, -0.5 }, { 0.68, 1.5 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 5, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "smoothOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 100, bezier = "liner", style = "loop" }) -- used by rainbow borders and rotating colors; speed clamped from 180 -> Lua hl.animation caps speed at 100
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 3, bezier = "smoothOut" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "overshot" })
|
||||
|
||||
-- animations for -git or version >0.42.0
|
||||
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 5, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
15
dotfiles/.config/hypr/presets/anim-ml4w-classic.lua
Normal file
15
dotfiles/.config/hypr/presets/anim-ml4w-classic.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Classic"
|
||||
-- credit https://github.com/mylinuxforwork/dotfiles
|
||||
-- Ported from animations/ML4W - classic.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("myBezier", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 7, bezier = "myBezier" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 7, bezier = "default", style = "popin 80%" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 8, bezier = "default" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 7, bezier = "default" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 6, bezier = "default" })
|
||||
20
dotfiles/.config/hypr/presets/anim-ml4w-dynamic.lua
Normal file
20
dotfiles/.config/hypr/presets/anim-ml4w-dynamic.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Dynamic"
|
||||
-- credit https://github.com/mylinuxforwork/dotfiles
|
||||
-- Ported from animations/ML4W - dynamic.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 6, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 30, bezier = "liner", style = "loop" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "wind" })
|
||||
24
dotfiles/.config/hypr/presets/anim-ml4w-fast.lua
Normal file
24
dotfiles/.config/hypr/presets/anim-ml4w-fast.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Fast"
|
||||
-- credit https://github.com/mylinuxforwork/dotfiles
|
||||
-- Ported from animations/ML4W - fast.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
|
||||
hl.curve("md3_standard", { type = "bezier", points = { { 0.2, 0 }, { 0, 1 } } })
|
||||
hl.curve("md3_decel", { type = "bezier", points = { { 0.05, 0.7 }, { 0.1, 1 } } })
|
||||
hl.curve("md3_accel", { type = "bezier", points = { { 0.3, 0 }, { 0.8, 0.15 } } })
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.1 } } })
|
||||
hl.curve("crazyshot", { type = "bezier", points = { { 0.1, 1.5 }, { 0.76, 0.92 } } })
|
||||
hl.curve("hyprnostretch", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.0 } } })
|
||||
hl.curve("fluent_decel", { type = "bezier", points = { { 0.1, 1 }, { 0, 1 } } })
|
||||
hl.curve("easeInOutCirc", { type = "bezier", points = { { 0.85, 0 }, { 0.15, 1 } } })
|
||||
hl.curve("easeOutCirc", { type = "bezier", points = { { 0, 0.55 }, { 0.45, 1 } } })
|
||||
hl.curve("easeOutExpo", { type = "bezier", points = { { 0.16, 1 }, { 0.3, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 3, bezier = "md3_decel", style = "popin 60%" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 2.5, bezier = "md3_decel" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 3.5, bezier = "easeOutExpo", style = "slide" })
|
||||
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 3, bezier = "md3_decel", style = "slidevert" })
|
||||
20
dotfiles/.config/hypr/presets/anim-ml4w-high.lua
Normal file
20
dotfiles/.config/hypr/presets/anim-ml4w-high.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "High"
|
||||
-- credit https://github.com/mylinuxforwork/dotfiles
|
||||
-- Ported from animations/ML4W - high.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("wind", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("winIn", { type = "bezier", points = { { 0.1, 1.1 }, { 0.1, 1.1 } } })
|
||||
hl.curve("winOut", { type = "bezier", points = { { 0.3, -0.3 }, { 0, 1 } } })
|
||||
hl.curve("liner", { type = "bezier", points = { { 1, 1 }, { 1, 1 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 6, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 6, bezier = "winIn", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 5, bezier = "winOut", style = "slide" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 5, bezier = "wind", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 1, bezier = "liner" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 30, bezier = "liner", style = "once" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 5, bezier = "wind" })
|
||||
19
dotfiles/.config/hypr/presets/anim-ml4w-moving.lua
Normal file
19
dotfiles/.config/hypr/presets/anim-ml4w-moving.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Moving"
|
||||
-- credit https://github.com/mylinuxforwork/dotfiles
|
||||
-- Ported from animations/ML4W - moving.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("overshot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
hl.curve("smoothOut", { type = "bezier", points = { { 0.5, 0 }, { 0.99, 0.99 } } })
|
||||
hl.curve("smoothIn", { type = "bezier", points = { { 0.5, -0.5 }, { 0.68, 1.5 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 5, bezier = "overshot", style = "slide" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "smoothOut" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 3, bezier = "smoothOut" })
|
||||
hl.animation({ leaf = "windowsMove", enabled = true, speed = 4, bezier = "smoothIn", style = "slide" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 5, bezier = "default" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 5, bezier = "smoothIn" })
|
||||
hl.animation({ leaf = "fadeDim", enabled = true, speed = 5, bezier = "smoothIn" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 6, bezier = "default" })
|
||||
15
dotfiles/.config/hypr/presets/anim-ml4w-standard.lua
Normal file
15
dotfiles/.config/hypr/presets/anim-ml4w-standard.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
-- HyprFlux — https://github.com/ahmad9059/HyprFlux
|
||||
-- name "Standard"
|
||||
-- credit https://github.com/mylinuxforwork/dotfiles
|
||||
-- Ported from animations/ML4W - standard.conf (unused preset — not required by anything)
|
||||
|
||||
hl.config({ animations = { enabled = true } })
|
||||
|
||||
hl.curve("myBezier", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.05 } } })
|
||||
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 7, bezier = "myBezier" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 7, bezier = "default", style = "popin 80%" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "borderangle", enabled = true, speed = 8, bezier = "default" })
|
||||
hl.animation({ leaf = "fade", enabled = true, speed = 7, bezier = "default" })
|
||||
hl.animation({ leaf = "workspaces", enabled = true, speed = 6, bezier = "default" })
|
||||
73
dotfiles/.config/hypr/scripts/FloatBitwardenPopup.sh
Executable file
73
dotfiles/.config/hypr/scripts/FloatBitwardenPopup.sh
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
# Float + resize (to 781x702) + center the Bitwarden browser-extension pop-out window.
|
||||
#
|
||||
# Why this exists instead of a window rule: Hyprland's `float`/`center`/`size`
|
||||
# rules are "static" — evaluated exactly once, at the instant a window is
|
||||
# created — and this popup opens with the generic title "Zen Browser" (same
|
||||
# initialTitle/initialClass as the main browser window), only updating to
|
||||
# "Extension: (Bitwarden Password Manager) - Bitwarden..." a moment after
|
||||
# it's already mapped. By then the static rule has already missed its one
|
||||
# chance and there's no field available at creation time to tell the popup
|
||||
# apart from a normal browser window. See layout/windowrules.lua and
|
||||
# https://github.com/hyprwm/Hyprland/issues/7402 for the same problem class.
|
||||
#
|
||||
# No socat/ncat is installed, so instead of an event-driven IPC listener this
|
||||
# just polls `hyprctl clients` at a short interval with jq (already
|
||||
# installed).
|
||||
#
|
||||
# Dispatch syntax note: Hyprland 0.55+'s Lua config changed `hyprctl dispatch`
|
||||
# to take a literal Lua expression calling hl.dsp.* (e.g.
|
||||
# `hyprctl dispatch 'hl.dsp.window.center()'`), not the old
|
||||
# "centerwindow"-style dispatcher strings. Verified live against the real
|
||||
# API in /usr/share/hypr/stubs/hl.meta.lua before wiring this in.
|
||||
#
|
||||
# Why it re-asserts for a few seconds instead of firing once: right after the
|
||||
# popup leaves the tiled layout, Hyprland briefly re-tiles the sibling window
|
||||
# to fill the freed space, and during that reflow this popup was observed
|
||||
# reporting `floating: false` again for one poll tick — snapping it back to
|
||||
# its old tiled geometry. Re-asserting float+center for a short settle window
|
||||
# after first detection rides that out. After the settle window it's left
|
||||
# alone, so a manual re-tile/move by you later isn't fought.
|
||||
|
||||
TITLE_PATTERN='Extension: \(Bitwarden Password Manager\)'
|
||||
POLL_INTERVAL=0.3
|
||||
SETTLE_TICKS=6 # ~2s of re-assertion after first detection, at POLL_INTERVAL
|
||||
TARGET_WIDTH=781
|
||||
TARGET_HEIGHT=702
|
||||
|
||||
declare -A ticks_left # address -> remaining re-assert ticks
|
||||
|
||||
while true; do
|
||||
sleep "$POLL_INTERVAL"
|
||||
|
||||
# Forget any tracked window that's no longer open.
|
||||
open_addresses=$(hyprctl clients -j | jq -r '.[].address')
|
||||
for addr in "${!ticks_left[@]}"; do
|
||||
grep -qF "$addr" <<<"$open_addresses" || unset 'ticks_left[$addr]'
|
||||
done
|
||||
|
||||
# Process substitution (not a pipe) so the while loop runs in this shell
|
||||
# and its writes to ticks_left actually persist across iterations.
|
||||
while IFS= read -r win; do
|
||||
title=$(jq -r '.title' <<<"$win")
|
||||
[[ "$title" =~ $TITLE_PATTERN ]] || continue
|
||||
|
||||
address=$(jq -r '.address' <<<"$win")
|
||||
floating=$(jq -r '.floating' <<<"$win")
|
||||
|
||||
# First time we've seen this window: start its settle window.
|
||||
[[ -v ticks_left[$address] ]] || ticks_left[$address]=$SETTLE_TICKS
|
||||
|
||||
# Already floating and past its settle window: leave it alone.
|
||||
[[ "$floating" == "true" && ${ticks_left[$address]} -le 0 ]] && continue
|
||||
|
||||
hyprctl dispatch "hl.dsp.focus({ window = \"address:$address\" })" >/dev/null
|
||||
hyprctl dispatch 'hl.dsp.window.float({ action = "enable" })' >/dev/null
|
||||
# resize({x=W,y=H}) sets the absolute size (not a delta) — verified live.
|
||||
# Must happen before center(), since resizing shifts what "centered" means.
|
||||
hyprctl dispatch "hl.dsp.window.resize({ x = $TARGET_WIDTH, y = $TARGET_HEIGHT })" >/dev/null
|
||||
hyprctl dispatch 'hl.dsp.window.center()' >/dev/null
|
||||
|
||||
ticks_left[$address]=$((ticks_left[$address] - 1))
|
||||
done < <(hyprctl clients -j | jq -c '.[] | {address, title, floating}')
|
||||
done
|
||||
@@ -31,6 +31,14 @@ console_title_template = '{{ .Shell }} in {{ .Folder }}'
|
||||
commit_icon = '@'
|
||||
fetch_status = true
|
||||
|
||||
[[blocks.segments]]
|
||||
type = "pnpm"
|
||||
style = "powerline"
|
||||
powerline_symbol = "❯"
|
||||
foreground = "blue"
|
||||
background = "transparent"
|
||||
template = " {{ .Full }} "
|
||||
|
||||
[[blocks.segments]]
|
||||
type = 'docker'
|
||||
style = 'powerline'
|
||||
|
||||
25
dotfiles/.config/quickshell/config/Appearance.qml
Normal file
25
dotfiles/.config/quickshell/config/Appearance.qml
Normal file
@@ -0,0 +1,25 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property string fontFamily: "CaskaydiaCove Nerd Font Mono"
|
||||
readonly property string iconFontFamily: "Material Symbols Outlined"
|
||||
// Material Symbols is a variable font. Left unset, glyphs fall back to the
|
||||
// font's default optical size (24px), which is tuned for larger rendering
|
||||
// and makes some icons (e.g. "lock") look heavier/bigger than others when
|
||||
// drawn small — pin "opsz" near the actual render size to keep icons
|
||||
// visually consistent. See Appearance.iconSize for the matching pixelSize.
|
||||
readonly property var iconFontAxes: ({ "FILL": 0, "wght": 400, "GRAD": 0, "opsz": 20 })
|
||||
readonly property int iconSize: 16
|
||||
|
||||
readonly property int fontSize: 13
|
||||
readonly property int fontSizeSmall: 11
|
||||
|
||||
readonly property int radius: 14
|
||||
readonly property int radiusSmall: 8
|
||||
|
||||
readonly property int barHeight: 34
|
||||
readonly property int spacing: 10
|
||||
|
||||
readonly property int animationDuration: 150
|
||||
}
|
||||
62
dotfiles/.config/quickshell/config/Colors.qml
Normal file
62
dotfiles/.config/quickshell/config/Colors.qml
Normal file
@@ -0,0 +1,62 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
// Wallpaper-driven palette, sourced from pywal's ~/.cache/wal/colors.json.
|
||||
// Run `wal -i /path/to/wallpaper.png` to (re)generate it — this file watches
|
||||
// the cache and updates live, no shell restart needed. Until that's been run
|
||||
// once, everything falls back to a hand-picked dark palette below.
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
readonly property bool active: _parsed !== null
|
||||
property var _parsed: null
|
||||
|
||||
// --- Fallback palette (used until pywal has run) ---
|
||||
readonly property color fallbackBg: "#1a1b26"
|
||||
readonly property color fallbackFg: "#c0caf5"
|
||||
readonly property var fallbackPalette: [
|
||||
"#15161e", "#f7768e", "#9ece6a", "#e0af68",
|
||||
"#7aa2f7", "#bb9af7", "#7dcfff", "#a9b1d6",
|
||||
"#414868", "#f7768e", "#9ece6a", "#e0af68",
|
||||
"#7aa2f7", "#bb9af7", "#7dcfff", "#c0caf5"
|
||||
]
|
||||
|
||||
function paletteColor(i) {
|
||||
if (_parsed && _parsed.colors)
|
||||
return _parsed.colors["color" + i] || fallbackPalette[i]
|
||||
return fallbackPalette[i]
|
||||
}
|
||||
|
||||
readonly property color background: _parsed && _parsed.special ? _parsed.special.background : fallbackBg
|
||||
readonly property color foreground: _parsed && _parsed.special ? _parsed.special.foreground : fallbackFg
|
||||
readonly property color primary: paletteColor(4)
|
||||
// Named primaryText (not "onPrimary") because QML treats any "onXxx"
|
||||
// identifier as a signal handler slot, not a regular property.
|
||||
readonly property color primaryText: background
|
||||
readonly property color urgent: paletteColor(1)
|
||||
// paletteColor(8) alone renders quite dark/low-contrast for text (it's
|
||||
// meant as a muted *background* tone in most wal palettes) — lighten it
|
||||
// so muted labels stay legible against the bar's dark background.
|
||||
readonly property color muted: Qt.lighter(paletteColor(8), 2.2)
|
||||
readonly property color surface: Qt.lighter(background, 1.35)
|
||||
|
||||
// QtObject has no default property, so the FileView is attached via a
|
||||
// property assignment rather than as a plain child element.
|
||||
readonly property FileView _walFile: FileView {
|
||||
path: Quickshell.env("HOME") + "/.cache/wal/colors.json"
|
||||
watchChanges: true
|
||||
onLoaded: {
|
||||
try {
|
||||
root._parsed = JSON.parse(text())
|
||||
} catch (e) {
|
||||
console.warn("Colors: failed to parse pywal cache:", e)
|
||||
root._parsed = null
|
||||
}
|
||||
}
|
||||
onLoadFailed: (error) => {
|
||||
root._parsed = null
|
||||
}
|
||||
}
|
||||
}
|
||||
16
dotfiles/.config/quickshell/config/VpnExclusivity.qml
Normal file
16
dotfiles/.config/quickshell/config/VpnExclusivity.qml
Normal file
@@ -0,0 +1,16 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
|
||||
// Tailscale, Mullvad, and ProtonVPN all fight over the default route/DNS if
|
||||
// more than one is active at once (see VpnIndicator.qml's header comment) —
|
||||
// only one of the three should ever actually be connected. This singleton
|
||||
// is the shared signal between the two widget files: VpnIndicator.qml
|
||||
// writes Mullvad/ProtonVPN's live connection state here as it polls them;
|
||||
// TailscaleIndicator.qml reads it to back off (`tailscale down`) rather
|
||||
// than stay up alongside either one, and to refuse turning Tailscale back
|
||||
// on while one is still connected.
|
||||
QtObject {
|
||||
property bool mullvadConnected: false
|
||||
property bool protonConnected: false
|
||||
readonly property bool commercialVpnActive: mullvadConnected || protonConnected
|
||||
}
|
||||
4
dotfiles/.config/quickshell/config/qmldir
Normal file
4
dotfiles/.config/quickshell/config/qmldir
Normal file
@@ -0,0 +1,4 @@
|
||||
module config
|
||||
singleton Colors 1.0 Colors.qml
|
||||
singleton Appearance 1.0 Appearance.qml
|
||||
singleton VpnExclusivity 1.0 VpnExclusivity.qml
|
||||
13
dotfiles/.config/quickshell/modules/bar/ActiveWindow.qml
Normal file
13
dotfiles/.config/quickshell/modules/bar/ActiveWindow.qml
Normal file
@@ -0,0 +1,13 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Hyprland
|
||||
import "../../config"
|
||||
|
||||
Text {
|
||||
Layout.maximumWidth: 320
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
text: Hyprland.activeToplevel ? Hyprland.activeToplevel.title : ""
|
||||
}
|
||||
57
dotfiles/.config/quickshell/modules/bar/Bar.qml
Normal file
57
dotfiles/.config/quickshell/modules/bar/Bar.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "../../config"
|
||||
|
||||
PanelWindow {
|
||||
id: bar
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
implicitHeight: Appearance.barHeight
|
||||
exclusiveZone: implicitHeight
|
||||
color: "transparent"
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.namespace: "quickshell:bar"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Colors.background
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: Appearance.spacing
|
||||
}
|
||||
spacing: Appearance.spacing
|
||||
Workspaces { screen: bar.screen }
|
||||
ActiveWindow {}
|
||||
}
|
||||
|
||||
Clock {
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
rightMargin: Appearance.spacing
|
||||
}
|
||||
spacing: Appearance.spacing
|
||||
//Tray {}
|
||||
TailscaleIndicator { barWindow: bar }
|
||||
VpnIndicator { barWindow: bar }
|
||||
NetworkIndicator { barWindow: bar }
|
||||
BluetoothIndicator { barWindow: bar }
|
||||
Volume { barWindow: bar }
|
||||
PowerButton { barWindow: bar }
|
||||
}
|
||||
}
|
||||
489
dotfiles/.config/quickshell/modules/bar/BluetoothIndicator.qml
Normal file
489
dotfiles/.config/quickshell/modules/bar/BluetoothIndicator.qml
Normal file
@@ -0,0 +1,489 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Bluetooth
|
||||
import "../../config"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: label.implicitWidth + 4
|
||||
implicitHeight: Appearance.barHeight
|
||||
|
||||
// The bar's own PanelWindow — passed in from Bar.qml so this popup can
|
||||
// anchor to the bar itself rather than to this icon, keeping it a fixed
|
||||
// distance from the bar and the screen edge regardless of where this
|
||||
// icon sits in the row.
|
||||
required property var barWindow
|
||||
|
||||
readonly property var adapter: Bluetooth.defaultAdapter
|
||||
|
||||
// Scan only while the panel is open — same convention as the WiFi
|
||||
// scannerEnabled binding in NetworkIndicator.qml.
|
||||
Binding {
|
||||
target: root.adapter
|
||||
property: "discovering"
|
||||
value: panel.visible && root.adapter.enabled
|
||||
when: root.adapter !== null
|
||||
}
|
||||
|
||||
// --- Device row helpers ----------------------------------------------
|
||||
// Rows are plain objects rather than the live BluetoothDevice QObject —
|
||||
// BlueZ churn (pairing timeouts, a device dropping out of range mid-scan)
|
||||
// can destroy the object while a delegate still holds it, the same
|
||||
// crash risk noted for WiFi networks in NetworkIndicator.qml. The actual
|
||||
// object is re-resolved by address only when an action needs it, via
|
||||
// deviceForAddress().
|
||||
function deviceLabel(d) {
|
||||
return (d.deviceName || d.name || d.address || "").trim()
|
||||
}
|
||||
|
||||
// BlueZ's Icon property is a freedesktop icon-theme name (verified
|
||||
// against a real paired device on this machine: "audio-headset") — map
|
||||
// the common categories to a Material Symbols glyph, falling back to a
|
||||
// generic "devices_other" for anything unrecognized.
|
||||
function deviceIcon(icon) {
|
||||
const i = icon || ""
|
||||
if (i.indexOf("headset") !== -1 || i.indexOf("headphone") !== -1) return "headphones"
|
||||
if (i.indexOf("mouse") !== -1) return "mouse"
|
||||
if (i.indexOf("keyboard") !== -1) return "keyboard"
|
||||
if (i.indexOf("gaming") !== -1 || i.indexOf("joystick") !== -1) return "sports_esports"
|
||||
if (i.indexOf("tablet") !== -1) return "tablet"
|
||||
if (i.indexOf("camera") !== -1) return "videocam"
|
||||
if (i.indexOf("printer") !== -1) return "print"
|
||||
if (i.indexOf("watch") !== -1) return "watch"
|
||||
if (i.indexOf("tv") !== -1) return "tv"
|
||||
if (i.indexOf("computer") !== -1) return "computer"
|
||||
if (i.indexOf("card") !== -1) return "speaker"
|
||||
return "devices_other"
|
||||
}
|
||||
|
||||
function deviceRow(d) {
|
||||
if (!d) return null
|
||||
const label = root.deviceLabel(d)
|
||||
if (!label) return null
|
||||
return {
|
||||
address: d.address || "",
|
||||
label: label,
|
||||
icon: root.deviceIcon(d.icon),
|
||||
connected: !!d.connected,
|
||||
paired: !!d.paired,
|
||||
bonded: !!d.bonded,
|
||||
trusted: !!d.trusted,
|
||||
pairing: !!d.pairing,
|
||||
batteryAvailable: !!d.batteryAvailable,
|
||||
battery: d.battery || 0
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var deviceRows: root.adapter
|
||||
? root.adapter.devices.values.map(root.deviceRow).filter(function(r) { return r !== null })
|
||||
: []
|
||||
|
||||
readonly property var connectedRows: root.deviceRows.filter(function(r) { return r.connected })
|
||||
readonly property var knownRows: root.deviceRows.filter(function(r) {
|
||||
return !r.connected && (r.paired || r.bonded || r.trusted)
|
||||
})
|
||||
readonly property var discoveredRows: root.deviceRows.filter(function(r) {
|
||||
return !r.connected && !r.paired && !r.bonded && !r.trusted
|
||||
})
|
||||
|
||||
function deviceForAddress(address) {
|
||||
if (!root.adapter) return null
|
||||
const devs = root.adapter.devices.values
|
||||
for (let i = 0; i < devs.length; i++) {
|
||||
if (devs[i].address === address) return devs[i]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function connectDevice(row) {
|
||||
const d = root.deviceForAddress(row.address)
|
||||
if (!d) return
|
||||
// Already-paired devices just reconnect; a fresh discovery result
|
||||
// has to pair first (BlueZ's own agent handles the SSP/passkey
|
||||
// handshake — there's no PIN-entry surface exposed by this API).
|
||||
if (row.paired || row.bonded || row.trusted) d.connect()
|
||||
else d.pair()
|
||||
}
|
||||
|
||||
function disconnectDevice(row) {
|
||||
const d = root.deviceForAddress(row.address)
|
||||
if (d) d.disconnect()
|
||||
}
|
||||
|
||||
function forgetDevice(row) {
|
||||
const d = root.deviceForAddress(row.address)
|
||||
if (!d) return
|
||||
if (d.connected) d.disconnect()
|
||||
d.forget()
|
||||
}
|
||||
|
||||
readonly property bool anyConnected: root.connectedRows.length > 0
|
||||
|
||||
// Caps how tall the Paired/Available list gets before it scrolls
|
||||
// instead of continuing to grow the popup — same convention as
|
||||
// NetworkIndicator.qml's Known/Other Networks list.
|
||||
readonly property int maxDeviceListHeight: 260
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: (root.adapter && root.adapter.enabled) ? Colors.foreground : Colors.muted
|
||||
text: {
|
||||
if (!root.adapter || !root.adapter.enabled) return "bluetooth_disabled"
|
||||
return root.anyConnected ? "bluetooth_connected" : "bluetooth"
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
panel.visible = !panel.visible
|
||||
grab.active = panel.visible
|
||||
}
|
||||
}
|
||||
|
||||
// --- Reusable pieces ---------------------------------------------------
|
||||
|
||||
component ToggleSwitch: Item {
|
||||
id: toggleRoot
|
||||
property bool checked: false
|
||||
signal toggled()
|
||||
implicitWidth: 34
|
||||
implicitHeight: 18
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: toggleRoot.checked ? Colors.primary : Colors.muted
|
||||
opacity: toggleRoot.checked ? 1 : 0.35
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.height - 4
|
||||
height: parent.height - 4
|
||||
radius: 0
|
||||
color: Colors.background
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: toggleRoot.checked ? parent.width - width - 2 : 2
|
||||
Behavior on x { NumberAnimation { duration: Appearance.animationDuration } }
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: toggleRoot.toggled()
|
||||
}
|
||||
}
|
||||
|
||||
// One device row. `showForget` hides the forget (x) button for
|
||||
// discovered-but-unpaired devices, which have nothing to forget yet.
|
||||
component DeviceRow: Rectangle {
|
||||
id: devRow
|
||||
required property var dev
|
||||
property bool showForget: true
|
||||
signal activate()
|
||||
width: parent ? parent.width : 0
|
||||
radius: 0
|
||||
implicitHeight: rowContent.implicitHeight + 12
|
||||
color: devRow.dev.connected
|
||||
? Qt.rgba(Colors.primary.r, Colors.primary.g, Colors.primary.b, 0.18)
|
||||
: (rowMouse.containsMouse ? Colors.primary : "transparent")
|
||||
|
||||
RowLayout {
|
||||
id: rowContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: 6
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
Layout.preferredWidth: 20
|
||||
Layout.minimumWidth: 20
|
||||
Layout.maximumWidth: 20
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: devRow.dev.icon
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 14
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: devRow.dev.label
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSize
|
||||
font.bold: devRow.dev.connected
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
Text {
|
||||
readonly property string statusText: {
|
||||
if (devRow.dev.pairing) return "Pairing…"
|
||||
if (devRow.dev.connected && devRow.dev.batteryAvailable)
|
||||
return Math.round(devRow.dev.battery * 100) + "%"
|
||||
if (devRow.dev.connected) return "Connected"
|
||||
return ""
|
||||
}
|
||||
visible: statusText !== ""
|
||||
text: statusText
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.muted
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: forgetIcon
|
||||
visible: devRow.showForget
|
||||
text: "close"
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 14
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.muted
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rowMouse
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
height: rowContent.height + 12
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
// forgetIcon has no MouseArea of its own — nested inside
|
||||
// rowContent, which sits underneath this full-row area in
|
||||
// stacking order, so a separate MouseArea there would just be
|
||||
// shadowed (see the identical fix in NetworkIndicator.qml's
|
||||
// NetworkRow). Dispatch by click position against this single
|
||||
// area instead.
|
||||
onClicked: (mouse) => {
|
||||
if (devRow.showForget && forgetIcon.visible) {
|
||||
const forgetLeft = devRow.mapFromItem(forgetIcon, 0, 0).x
|
||||
if (mouse.x >= forgetLeft - 8) {
|
||||
root.forgetDevice(devRow.dev)
|
||||
return
|
||||
}
|
||||
}
|
||||
devRow.activate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Panel -------------------------------------------------------------
|
||||
|
||||
PopupWindow {
|
||||
id: panel
|
||||
visible: false
|
||||
color: "transparent"
|
||||
readonly property int contentWidth: 244
|
||||
readonly property int contentPadding: 16
|
||||
implicitWidth: contentWidth + contentPadding * 2
|
||||
implicitHeight: mainColumn.implicitHeight + contentPadding * 2
|
||||
// Connected/paired/discovered devices come and go while the panel
|
||||
// is open — animate the resize instead of snapping, same as the
|
||||
// network panel's WiFi list.
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation { duration: Appearance.animationDuration; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
anchor {
|
||||
window: root.barWindow
|
||||
rect.x: 0
|
||||
rect.y: 0
|
||||
rect.width: root.barWindow.width
|
||||
rect.height: root.barWindow.height
|
||||
edges: Edges.Bottom | Edges.Right
|
||||
gravity: Edges.Bottom | Edges.Left
|
||||
margins.bottom: -4
|
||||
margins.right: 0
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [panel]
|
||||
onCleared: panel.visible = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: Colors.surface
|
||||
border.color: Colors.muted
|
||||
border.width: 3
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: panel.contentPadding
|
||||
spacing: 10
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: label.text
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 30
|
||||
color: Colors.foreground
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Bluetooth"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: Colors.foreground
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
visible: root.adapter !== null
|
||||
checked: root.adapter !== null && root.adapter.enabled
|
||||
onToggled: root.adapter.enabled = !root.adapter.enabled
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: root.adapter === null
|
||||
text: "No Bluetooth adapter"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: root.adapter !== null && !root.adapter.enabled
|
||||
text: "Turn Bluetooth on to see devices"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
|
||||
// Connected — always shown in full above the scrollable
|
||||
// list, mirroring the network panel's layout.
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
visible: root.connectedRows.length > 0
|
||||
|
||||
Text {
|
||||
text: "Connected"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
Repeater {
|
||||
model: root.connectedRows
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
dev: modelData
|
||||
showForget: true
|
||||
onActivate: root.disconnectDevice(dev)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: root.adapter !== null && root.adapter.enabled && root.deviceRows.length === 0
|
||||
text: (root.adapter && root.adapter.discovering) ? "Scanning for devices…" : "No devices found"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
|
||||
// Paired/Available — capped height, scrolls once there are
|
||||
// more devices than fit rather than growing the popup
|
||||
// indefinitely.
|
||||
Flickable {
|
||||
id: deviceListFlick
|
||||
width: parent.width
|
||||
height: Math.min(deviceListColumn.implicitHeight, root.maxDeviceListHeight)
|
||||
contentWidth: width
|
||||
contentHeight: deviceListColumn.implicitHeight
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
visible: deviceListColumn.implicitHeight > 0
|
||||
|
||||
Column {
|
||||
id: deviceListColumn
|
||||
width: deviceListFlick.width
|
||||
spacing: 6
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
visible: root.knownRows.length > 0
|
||||
|
||||
Text {
|
||||
text: "Paired Devices"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
Repeater {
|
||||
model: root.knownRows
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
dev: modelData
|
||||
showForget: true
|
||||
onActivate: root.connectDevice(dev)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
visible: root.adapter !== null && root.adapter.discovering && root.discoveredRows.length > 0
|
||||
|
||||
Text {
|
||||
text: "Available Devices"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
Repeater {
|
||||
model: root.discoveredRows
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
dev: modelData
|
||||
showForget: false
|
||||
onActivate: root.connectDevice(dev)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
dotfiles/.config/quickshell/modules/bar/Clock.qml
Normal file
18
dotfiles/.config/quickshell/modules/bar/Clock.qml
Normal file
@@ -0,0 +1,18 @@
|
||||
import QtQuick
|
||||
import "../../config"
|
||||
|
||||
Text {
|
||||
id: root
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSize
|
||||
font.bold: true
|
||||
color: Colors.foreground
|
||||
text: Qt.formatDateTime(new Date(), "ddd d MMM hh:mm")
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: root.text = Qt.formatDateTime(new Date(), "ddd d MMM hh:mm")
|
||||
}
|
||||
}
|
||||
1125
dotfiles/.config/quickshell/modules/bar/NetworkIndicator.qml
Normal file
1125
dotfiles/.config/quickshell/modules/bar/NetworkIndicator.qml
Normal file
File diff suppressed because it is too large
Load Diff
179
dotfiles/.config/quickshell/modules/bar/PowerButton.qml
Normal file
179
dotfiles/.config/quickshell/modules/bar/PowerButton.qml
Normal file
@@ -0,0 +1,179 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
import "../../config"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: label.implicitWidth + 4
|
||||
implicitHeight: Appearance.barHeight
|
||||
|
||||
// The bar's own PanelWindow — passed in from Bar.qml so this popup can
|
||||
// anchor to the bar itself rather than to this icon, keeping it a fixed
|
||||
// distance from the bar and the screen edge regardless of where this
|
||||
// icon sits in the row.
|
||||
required property var barWindow
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: Colors.foreground
|
||||
text: "power_settings_new"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
menu.visible = !menu.visible
|
||||
grab.active = menu.visible
|
||||
}
|
||||
}
|
||||
|
||||
Process { id: proc }
|
||||
|
||||
PopupWindow {
|
||||
id: menu
|
||||
visible: false
|
||||
color: "transparent"
|
||||
implicitWidth: 210
|
||||
// Same padding as Volume.qml's panel.
|
||||
readonly property int contentPadding: 16
|
||||
readonly property int labelOffset: -135
|
||||
implicitHeight: mainColumn.implicitHeight + contentPadding * 2
|
||||
|
||||
anchor {
|
||||
window: root.barWindow
|
||||
// Unlike anchor.item, anchor.window doesn't default rect to the
|
||||
// window's own bounds — set it explicitly so edges/gravity below
|
||||
// resolve against the full bar (and thus the true screen edges)
|
||||
// instead of a near-zero rect at the window's origin.
|
||||
rect.x: 0
|
||||
rect.y: 0
|
||||
rect.width: root.barWindow.width
|
||||
rect.height: root.barWindow.height
|
||||
edges: Edges.Bottom | Edges.Right
|
||||
gravity: Edges.Bottom | Edges.Left
|
||||
margins.bottom: -4
|
||||
margins.right: 0
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [menu]
|
||||
onCleared: menu.visible = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: Colors.surface
|
||||
border.color: Colors.muted
|
||||
border.width: 3
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: menu.contentPadding
|
||||
spacing: 10
|
||||
|
||||
// Header — same title layout as Volume.qml's panel: the bar
|
||||
// icon large, plus a bold title.
|
||||
RowLayout {
|
||||
id: header
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: label.text
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 30
|
||||
color: Colors.foreground
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Power"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: Colors.foreground
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
id: list
|
||||
model: [
|
||||
{ label: "Lock", icon: "lock", cmd: ["hyprlock"] },
|
||||
{ label: "Suspend", icon: "bedtime", cmd: ["systemctl", "suspend"] },
|
||||
{ label: "Hibernate", icon: "ac_unit", cmd: ["systemctl", "hibernate"] },
|
||||
{ label: "Log out", icon: "logout", cmd: ["hyprctl", "dispatch", "exit"] },
|
||||
{ label: "Reboot", icon: "restart_alt", cmd: ["systemctl", "reboot"] },
|
||||
{ label: "Shut down", icon: "power_settings_new", cmd: ["systemctl", "poweroff"] }
|
||||
]
|
||||
|
||||
delegate: Rectangle {
|
||||
id: entry
|
||||
required property var modelData
|
||||
width: parent ? parent.width : 0
|
||||
height: 40
|
||||
radius: Appearance.radiusSmall
|
||||
color: (!entry.modelData.disabled && entryMouse.containsMouse) ? Colors.primary : "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 8
|
||||
spacing: 8
|
||||
Text {
|
||||
Layout.preferredWidth: 25
|
||||
Layout.minimumWidth: 25
|
||||
Layout.maximumWidth: 25
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: entry.modelData.icon
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: entry.modelData.iconSize || 15
|
||||
opacity: entry.modelData.disabled ? 0.4 : 1
|
||||
color: entryMouse.containsMouse ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
Text {
|
||||
Layout.leftMargin: menu.labelOffset + (entry.modelData.textOffset || 0)
|
||||
text: entry.modelData.label
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: 16
|
||||
opacity: entry.modelData.disabled ? 0.4 : 1
|
||||
color: entryMouse.containsMouse ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: entryMouse
|
||||
anchors.fill: parent
|
||||
enabled: !entry.modelData.disabled
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
proc.command = entry.modelData.cmd
|
||||
proc.running = true
|
||||
menu.visible = false
|
||||
grab.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1095
dotfiles/.config/quickshell/modules/bar/TailscaleIndicator.qml
Normal file
1095
dotfiles/.config/quickshell/modules/bar/TailscaleIndicator.qml
Normal file
File diff suppressed because it is too large
Load Diff
32
dotfiles/.config/quickshell/modules/bar/Tray.qml
Normal file
32
dotfiles/.config/quickshell/modules/bar/Tray.qml
Normal file
@@ -0,0 +1,32 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.SystemTray
|
||||
import Quickshell.Widgets
|
||||
import "../../config"
|
||||
|
||||
RowLayout {
|
||||
spacing: Appearance.spacing * 0.6
|
||||
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
|
||||
delegate: IconImage {
|
||||
id: trayIcon
|
||||
required property var modelData
|
||||
implicitWidth: 16
|
||||
implicitHeight: 16
|
||||
source: modelData.icon
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: (mouse) => {
|
||||
if (mouse.button === Qt.LeftButton)
|
||||
trayIcon.modelData.activate()
|
||||
else
|
||||
trayIcon.modelData.secondaryActivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
446
dotfiles/.config/quickshell/modules/bar/Volume.qml
Normal file
446
dotfiles/.config/quickshell/modules/bar/Volume.qml
Normal file
@@ -0,0 +1,446 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.Pipewire
|
||||
import "../../config"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: label.implicitWidth + 4
|
||||
implicitHeight: Appearance.barHeight
|
||||
|
||||
// The bar's own PanelWindow — passed in from Bar.qml so this popup can
|
||||
// anchor to the bar itself rather than to this icon, keeping it a fixed
|
||||
// distance from the bar and the screen edge regardless of where this
|
||||
// icon sits in the row.
|
||||
required property var barWindow
|
||||
|
||||
readonly property var sink: Pipewire.defaultAudioSink
|
||||
readonly property var source: Pipewire.defaultAudioSource
|
||||
readonly property var nodes: Pipewire.nodes ? Pipewire.nodes.values : []
|
||||
|
||||
readonly property real outputVolume: (root.sink && root.sink.audio) ? root.sink.audio.volume : 0
|
||||
readonly property bool outputMuted: (root.sink && root.sink.audio) ? root.sink.audio.muted : false
|
||||
readonly property real inputVolume: (root.source && root.source.audio) ? root.source.audio.volume : 0
|
||||
readonly property bool inputMuted: (root.source && root.source.audio) ? root.source.audio.muted : false
|
||||
|
||||
readonly property var candidateSinks: root.nodes.filter(function(n) { return n && n.isSink && !n.isStream })
|
||||
readonly property var candidateSources: root.nodes.filter(function(n) { return n && !n.isSink && !n.isStream && n.audio })
|
||||
|
||||
PwObjectTracker { objects: root.candidateSinks }
|
||||
PwObjectTracker { objects: root.candidateSources }
|
||||
|
||||
// Live mic level for the input gauge below — separate from inputVolume
|
||||
// (the configured gain), this tracks the actual incoming signal.
|
||||
// Only runs while the panel is open, to skip metering when unused.
|
||||
PwNodePeakMonitor {
|
||||
id: inputPeak
|
||||
node: root.source
|
||||
enabled: panel.visible
|
||||
}
|
||||
|
||||
// --- Device label/icon heuristics, ported from omarchy's audio panel
|
||||
// Model.js (github.com/basecamp/omarchy shell/plugins/panels/audio/Model.js),
|
||||
// adapted to this project's Material Symbols icon set instead of Nerd Font.
|
||||
function friendlyDeviceLabel(text) {
|
||||
var out = String(text || "").trim()
|
||||
out = out.replace(/^sof-soundwire\s+/i, "")
|
||||
out = out.replace(/^built-?in audio\s+/i, "")
|
||||
out = out.replace(/\s+Output$/i, "")
|
||||
out = out.replace(/\s+Input$/i, "")
|
||||
out = out.replace(/Microphones/gi, "Microphone")
|
||||
return out
|
||||
}
|
||||
|
||||
function deviceLabel(node) {
|
||||
if (!node) return "Unknown"
|
||||
var nick = friendlyDeviceLabel(node.nickname || "")
|
||||
if (nick) return nick
|
||||
return friendlyDeviceLabel(node.description || node.name || "Unknown")
|
||||
}
|
||||
|
||||
function deviceBlob(node) {
|
||||
if (!node) return ""
|
||||
var p = node.properties || {}
|
||||
return [node.name, node.description, node.nickname,
|
||||
p["device.icon-name"] || "", p["device.product.name"] || ""].join(" ").toLowerCase()
|
||||
}
|
||||
|
||||
function isHeadphoneNode(node) {
|
||||
var blob = deviceBlob(node)
|
||||
return blob.indexOf("headphone") !== -1 || blob.indexOf("headset") !== -1
|
||||
|| blob.indexOf("earbud") !== -1 || blob.indexOf("earphone") !== -1
|
||||
|| blob.indexOf("airpod") !== -1
|
||||
}
|
||||
|
||||
function sinkIcon(node) {
|
||||
if (!node) return "speaker"
|
||||
if (isHeadphoneNode(node)) return "headphones"
|
||||
var blob = deviceBlob(node)
|
||||
if (blob.indexOf("bluetooth") !== -1) return "bluetooth_audio"
|
||||
if (blob.indexOf("hdmi") !== -1 || blob.indexOf("display") !== -1) return "tv"
|
||||
return "speaker"
|
||||
}
|
||||
|
||||
function sourceIcon(node) {
|
||||
if (!node) return "mic"
|
||||
var blob = deviceBlob(node)
|
||||
if (blob.indexOf("headset") !== -1) return "headphones"
|
||||
if (blob.indexOf("bluetooth") !== -1) return "bluetooth_audio"
|
||||
if (blob.indexOf("webcam") !== -1 || blob.indexOf("camera") !== -1) return "videocam"
|
||||
return "mic"
|
||||
}
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 16
|
||||
color: Colors.foreground
|
||||
text: (root.outputMuted || root.outputVolume <= 0) ? "volume_off"
|
||||
: (root.outputVolume < 0.5 ? "volume_down" : "volume_up")
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
panel.visible = !panel.visible
|
||||
grab.active = panel.visible
|
||||
}
|
||||
onWheel: (wheel) => {
|
||||
if (!root.sink || !root.sink.audio) return
|
||||
const step = 0.05
|
||||
const delta = wheel.angleDelta.y > 0 ? step : -step
|
||||
root.sink.audio.volume = Math.max(0, Math.min(1, root.sink.audio.volume + delta))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Reusable pieces -----------------------------------------------
|
||||
|
||||
component VolumeSlider: Item {
|
||||
id: sliderRoot
|
||||
property real value: 0
|
||||
property bool enabled: true
|
||||
signal moved(real newValue)
|
||||
implicitHeight: 16
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 4
|
||||
radius: 0
|
||||
color: Colors.muted
|
||||
opacity: 0.35
|
||||
}
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
width: track.width * Math.max(0, Math.min(1, sliderRoot.value))
|
||||
height: 4
|
||||
radius: 0
|
||||
color: Colors.primary
|
||||
opacity: sliderRoot.enabled ? 1 : 0.4
|
||||
}
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: Colors.primary
|
||||
opacity: sliderRoot.enabled ? 1 : 0.4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: Math.max(0, Math.min(track.width - width, track.width * sliderRoot.value - width / 2))
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: sliderRoot.enabled
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
function updateFromX(mx) {
|
||||
sliderRoot.moved(Math.max(0, Math.min(1, mx / track.width)))
|
||||
}
|
||||
onPressed: (mouse) => updateFromX(mouse.x)
|
||||
onPositionChanged: (mouse) => { if (pressed) updateFromX(mouse.x) }
|
||||
}
|
||||
}
|
||||
|
||||
// Read-only live level gauge — same track styling as VolumeSlider but no
|
||||
// handle/interaction, filled from an actual signal level instead of a
|
||||
// configured value.
|
||||
component LevelMeter: Item {
|
||||
id: meterRoot
|
||||
property real level: 0
|
||||
implicitHeight: 4
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 4
|
||||
radius: 0
|
||||
color: Colors.muted
|
||||
opacity: 0.25
|
||||
}
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
height: 4
|
||||
radius: 0
|
||||
color: Colors.paletteColor(2)
|
||||
width: parent.width * Math.max(0, Math.min(1, meterRoot.level))
|
||||
Behavior on width { NumberAnimation { duration: 80 } }
|
||||
}
|
||||
}
|
||||
|
||||
component ToggleSwitch: Item {
|
||||
id: toggleRoot
|
||||
property bool checked: false
|
||||
signal toggled()
|
||||
implicitWidth: 34
|
||||
implicitHeight: 18
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: toggleRoot.checked ? Colors.primary : Colors.muted
|
||||
opacity: toggleRoot.checked ? 1 : 0.35
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.height - 4
|
||||
height: parent.height - 4
|
||||
radius: 0
|
||||
color: Colors.background
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: toggleRoot.checked ? parent.width - width - 2 : 2
|
||||
Behavior on x { NumberAnimation { duration: Appearance.animationDuration } }
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: toggleRoot.toggled()
|
||||
}
|
||||
}
|
||||
|
||||
component DeviceRow: Rectangle {
|
||||
id: deviceRow
|
||||
required property var node
|
||||
required property string icon
|
||||
required property bool active
|
||||
signal activate()
|
||||
width: parent ? parent.width : 0
|
||||
height: 30
|
||||
radius: 0
|
||||
color: rowMouse.containsMouse ? Colors.primary
|
||||
: (deviceRow.active ? Qt.rgba(Colors.primary.r, Colors.primary.g, Colors.primary.b, 0.18) : "transparent")
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 8
|
||||
anchors.rightMargin: 8
|
||||
spacing: 8
|
||||
Text {
|
||||
Layout.preferredWidth: 18
|
||||
Layout.minimumWidth: 18
|
||||
Layout.maximumWidth: 18
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: deviceRow.icon
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 14
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: root.deviceLabel(deviceRow.node)
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSize
|
||||
font.bold: deviceRow.active
|
||||
color: rowMouse.containsMouse ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: rowMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: deviceRow.activate()
|
||||
}
|
||||
}
|
||||
|
||||
// --- Panel -----------------------------------------------------------
|
||||
|
||||
PopupWindow {
|
||||
id: panel
|
||||
visible: false
|
||||
color: "transparent"
|
||||
// Padding between the panel's border and its content. implicitWidth
|
||||
// is contentWidth + padding on both sides, so the content area
|
||||
// itself stays a fixed size regardless of how much padding surrounds it.
|
||||
readonly property int contentWidth: 244
|
||||
readonly property int contentPadding: 16
|
||||
implicitWidth: contentWidth + contentPadding * 2
|
||||
implicitHeight: mainColumn.implicitHeight + contentPadding * 2
|
||||
|
||||
anchor {
|
||||
window: root.barWindow
|
||||
// Unlike anchor.item, anchor.window doesn't default rect to the
|
||||
// window's own bounds — set it explicitly so edges/gravity below
|
||||
// resolve against the full bar (and thus the true screen edges)
|
||||
// instead of a near-zero rect at the window's origin.
|
||||
rect.x: 0
|
||||
rect.y: 0
|
||||
rect.width: root.barWindow.width
|
||||
rect.height: root.barWindow.height
|
||||
edges: Edges.Bottom | Edges.Right
|
||||
gravity: Edges.Bottom | Edges.Left
|
||||
margins.bottom: -4
|
||||
margins.right: 0
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [panel]
|
||||
onCleared: panel.visible = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: Colors.surface
|
||||
border.color: Colors.muted
|
||||
border.width: 3
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: panel.contentPadding
|
||||
spacing: 10
|
||||
|
||||
// Header
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: label.text
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 40
|
||||
color: Colors.foreground
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Audio"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: Colors.foreground
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
checked: !root.outputMuted
|
||||
onToggled: if (root.sink && root.sink.audio) root.sink.audio.muted = !root.sink.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "OUTPUT"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
Text {
|
||||
text: Math.round(root.outputVolume * 100) + "%"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
}
|
||||
|
||||
VolumeSlider {
|
||||
width: parent.width
|
||||
value: root.outputVolume
|
||||
enabled: !!root.sink
|
||||
onMoved: (v) => { if (root.sink && root.sink.audio) root.sink.audio.volume = v }
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
Repeater {
|
||||
model: root.candidateSinks
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
node: modelData
|
||||
icon: root.sinkIcon(modelData)
|
||||
active: !!(root.sink && modelData && root.sink.id === modelData.id)
|
||||
onActivate: Pipewire.preferredDefaultAudioSink = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "INPUT"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
Text {
|
||||
text: Math.round(root.inputVolume * 100) + "%"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
}
|
||||
|
||||
VolumeSlider {
|
||||
width: parent.width
|
||||
value: root.inputVolume
|
||||
enabled: !!root.source
|
||||
onMoved: (v) => { if (root.source && root.source.audio) root.source.audio.volume = v }
|
||||
}
|
||||
|
||||
LevelMeter {
|
||||
width: parent.width
|
||||
level: inputPeak.peak
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
Repeater {
|
||||
model: root.candidateSources
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
node: modelData
|
||||
icon: root.sourceIcon(modelData)
|
||||
active: !!(root.source && modelData && root.source.id === modelData.id)
|
||||
onActivate: Pipewire.preferredDefaultAudioSource = modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
554
dotfiles/.config/quickshell/modules/bar/VpnIndicator.qml
Normal file
554
dotfiles/.config/quickshell/modules/bar/VpnIndicator.qml
Normal file
@@ -0,0 +1,554 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../config"
|
||||
|
||||
// A switcher between commercial VPN providers, distinct from
|
||||
// TailscaleIndicator.qml — Tailscale is a mesh overlay network you generally
|
||||
// leave running; these are traditional single-tunnel VPN clients where only
|
||||
// one makes sense to have active at a time. Connecting one here disconnects
|
||||
// the other first.
|
||||
//
|
||||
// V1 scope: connect (provider's own "fastest"/default) and disconnect per
|
||||
// provider, live status, no region/server picker yet. Provider CLIs:
|
||||
// - Mullvad: official `mullvad` CLI (mullvad-vpn-daemon package), has
|
||||
// real JSON status output.
|
||||
// - ProtonVPN: official `protonvpn` CLI (proton-vpn-cli package), plain-
|
||||
// text status only — parsed defensively since its exact wording isn't
|
||||
// guaranteed across versions.
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: label.implicitWidth + 4
|
||||
implicitHeight: Appearance.barHeight
|
||||
|
||||
required property var barWindow
|
||||
|
||||
property int refreshIntervalSec: 20
|
||||
readonly property int _refreshIntervalMs: Math.max(5, Math.min(3600, refreshIntervalSec)) * 1000
|
||||
|
||||
// Whether each CLI is on PATH at all. Starts optimistic so the panel
|
||||
// doesn't flash "not installed" before the first `which` check lands.
|
||||
property bool mullvadInstalled: true
|
||||
property bool protonInstalled: true
|
||||
|
||||
// "disconnected" | "connecting" | "connected" | "disconnecting"
|
||||
property string mullvadState: "disconnected"
|
||||
property string mullvadDetail: ""
|
||||
property string protonState: "disconnected"
|
||||
property string protonDetail: ""
|
||||
|
||||
readonly property bool anyConnected: mullvadState === "connected" || protonState === "connected"
|
||||
|
||||
// Set while switching from one provider to the other: holds which one
|
||||
// to connect once the current one has finished disconnecting.
|
||||
property string pendingConnect: ""
|
||||
|
||||
// Surfaced under the header for a failed connect/disconnect; cleared
|
||||
// automatically after a few seconds.
|
||||
property string actionStatus: ""
|
||||
|
||||
function shortLine(text) {
|
||||
const line = String(text || "").trim().split("\n")[0] || ""
|
||||
return line.length > 80 ? line.substring(0, 77) + "…" : line
|
||||
}
|
||||
|
||||
function applyMullvadStatus(text) {
|
||||
try {
|
||||
const data = JSON.parse(text)
|
||||
const state = String(data.state || "disconnected")
|
||||
const loc = data.details && data.details.location
|
||||
root.mullvadState = state
|
||||
root.mullvadDetail = loc ? [loc.city, loc.country].filter(Boolean).join(", ") : ""
|
||||
} catch (e) {
|
||||
root.mullvadState = "disconnected"
|
||||
root.mullvadDetail = ""
|
||||
}
|
||||
VpnExclusivity.mullvadConnected = root.mullvadState === "connected"
|
||||
}
|
||||
|
||||
// No --json here — ProtonVPN's CLI only prints plain text, so this
|
||||
// just looks for a "Status: …" line and, best-effort, a following
|
||||
// "Server:"/"Country:" line if the CLI happens to print one.
|
||||
function applyProtonStatus(text) {
|
||||
const body = String(text || "")
|
||||
const statusMatch = body.match(/^Status:\s*(.+)$/mi)
|
||||
const raw = statusMatch ? statusMatch[1].trim().toLowerCase() : ""
|
||||
root.protonState = raw.indexOf("connected") === 0 ? "connected" : "disconnected"
|
||||
const serverMatch = body.match(/^Server:\s*(.+)$/mi)
|
||||
const countryMatch = body.match(/^Country:\s*(.+)$/mi)
|
||||
root.protonDetail = serverMatch ? serverMatch[1].trim() : (countryMatch ? countryMatch[1].trim() : "")
|
||||
VpnExclusivity.protonConnected = root.protonState === "connected"
|
||||
}
|
||||
|
||||
function refreshMullvad() {
|
||||
if (!root.mullvadInstalled) {
|
||||
if (!mullvadWhichProc.running) mullvadWhichProc.running = true
|
||||
return
|
||||
}
|
||||
if (!mullvadStatusProc.running) {
|
||||
mullvadStatusProc.running = true
|
||||
mullvadPollWatchdog.restart()
|
||||
}
|
||||
}
|
||||
|
||||
function refreshProton() {
|
||||
if (!root.protonInstalled) {
|
||||
if (!protonWhichProc.running) protonWhichProc.running = true
|
||||
return
|
||||
}
|
||||
if (!protonStatusProc.running) {
|
||||
protonStatusProc.running = true
|
||||
protonPollWatchdog.restart()
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
root.refreshMullvad()
|
||||
root.refreshProton()
|
||||
}
|
||||
|
||||
function connectMullvad() {
|
||||
if (mullvadConnectProc.running || mullvadDisconnectProc.running) return
|
||||
if (root.protonState !== "disconnected") {
|
||||
root.pendingConnect = "mullvad"
|
||||
root.disconnectProton()
|
||||
return
|
||||
}
|
||||
root.pendingConnect = ""
|
||||
mullvadConnectProc.running = true
|
||||
}
|
||||
|
||||
function connectProton() {
|
||||
if (protonConnectProc.running || protonDisconnectProc.running) return
|
||||
if (root.mullvadState !== "disconnected") {
|
||||
root.pendingConnect = "protonvpn"
|
||||
root.disconnectMullvad()
|
||||
return
|
||||
}
|
||||
root.pendingConnect = ""
|
||||
protonConnectProc.running = true
|
||||
}
|
||||
|
||||
function disconnectMullvad() {
|
||||
if (mullvadDisconnectProc.running) return
|
||||
mullvadDisconnectProc.running = true
|
||||
}
|
||||
|
||||
function disconnectProton() {
|
||||
if (protonDisconnectProc.running) return
|
||||
protonDisconnectProc.running = true
|
||||
}
|
||||
|
||||
function toggleMullvad() {
|
||||
if (mullvadConnectProc.running || mullvadDisconnectProc.running) return
|
||||
if (root.mullvadState === "connected") root.disconnectMullvad()
|
||||
else root.connectMullvad()
|
||||
}
|
||||
|
||||
function toggleProton() {
|
||||
if (protonConnectProc.running || protonDisconnectProc.running) return
|
||||
if (root.protonState === "connected") root.disconnectProton()
|
||||
else root.connectProton()
|
||||
}
|
||||
|
||||
// Tailscale fights Mullvad/ProtonVPN for the default route/DNS, so fire
|
||||
// this right after either one finishes connecting — a harmless no-op
|
||||
// if Tailscale was already down. TailscaleIndicator.qml also backs
|
||||
// itself off reactively (via VpnExclusivity) as a backstop, but doing
|
||||
// it here too avoids waiting out its poll interval.
|
||||
Process {
|
||||
id: tailscaleDownProc
|
||||
command: ["tailscale", "down"]
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
|
||||
// --- Mullvad ---------------------------------------------------------
|
||||
|
||||
Process {
|
||||
id: mullvadWhichProc
|
||||
command: ["which", "mullvad"]
|
||||
running: true
|
||||
stdout: StdioCollector {}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.mullvadInstalled = exitCode === 0
|
||||
if (root.mullvadInstalled) root.refreshMullvad()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mullvadStatusProc
|
||||
command: ["mullvad", "status", "--json"]
|
||||
stdout: StdioCollector {
|
||||
id: mullvadStatusCollector
|
||||
onStreamFinished: root.applyMullvadStatus(mullvadStatusCollector.text)
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0 && !mullvadWhichProc.running) mullvadWhichProc.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mullvadConnectProc
|
||||
command: ["mullvad", "connect", "--wait"]
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector { id: mullvadConnectErr }
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0) {
|
||||
root.actionStatus = "Mullvad: " + root.shortLine(mullvadConnectErr.text || "connect failed")
|
||||
actionStatusTimer.restart()
|
||||
} else if (!tailscaleDownProc.running) {
|
||||
tailscaleDownProc.running = true
|
||||
}
|
||||
root.refreshMullvad()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: mullvadDisconnectProc
|
||||
command: ["mullvad", "disconnect", "--wait"]
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector { id: mullvadDisconnectErr }
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0) {
|
||||
root.actionStatus = "Mullvad: " + root.shortLine(mullvadDisconnectErr.text || "disconnect failed")
|
||||
actionStatusTimer.restart()
|
||||
}
|
||||
root.refreshMullvad()
|
||||
if (root.pendingConnect === "mullvad") { root.pendingConnect = ""; root.connectMullvad() }
|
||||
else if (root.pendingConnect === "protonvpn") { root.pendingConnect = ""; root.connectProton() }
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: mullvadPollWatchdog
|
||||
interval: 15000
|
||||
repeat: false
|
||||
onTriggered: if (mullvadStatusProc.running) mullvadStatusProc.running = false
|
||||
}
|
||||
|
||||
// --- ProtonVPN ---------------------------------------------------------
|
||||
|
||||
Process {
|
||||
id: protonWhichProc
|
||||
command: ["which", "protonvpn"]
|
||||
running: true
|
||||
stdout: StdioCollector {}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.protonInstalled = exitCode === 0
|
||||
if (root.protonInstalled) root.refreshProton()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: protonStatusProc
|
||||
command: ["protonvpn", "status"]
|
||||
stdout: StdioCollector {
|
||||
id: protonStatusCollector
|
||||
onStreamFinished: root.applyProtonStatus(protonStatusCollector.text)
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0 && !protonWhichProc.running) protonWhichProc.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: protonConnectProc
|
||||
command: ["protonvpn", "connect"]
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector { id: protonConnectErr }
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0) {
|
||||
root.actionStatus = "ProtonVPN: " + root.shortLine(protonConnectErr.text || "connect failed")
|
||||
actionStatusTimer.restart()
|
||||
} else if (!tailscaleDownProc.running) {
|
||||
tailscaleDownProc.running = true
|
||||
}
|
||||
root.refreshProton()
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: protonDisconnectProc
|
||||
command: ["protonvpn", "disconnect"]
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector { id: protonDisconnectErr }
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode !== 0) {
|
||||
root.actionStatus = "ProtonVPN: " + root.shortLine(protonDisconnectErr.text || "disconnect failed")
|
||||
actionStatusTimer.restart()
|
||||
}
|
||||
root.refreshProton()
|
||||
if (root.pendingConnect === "mullvad") { root.pendingConnect = ""; root.connectMullvad() }
|
||||
else if (root.pendingConnect === "protonvpn") { root.pendingConnect = ""; root.connectProton() }
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: protonPollWatchdog
|
||||
interval: 15000
|
||||
repeat: false
|
||||
onTriggered: if (protonStatusProc.running) protonStatusProc.running = false
|
||||
}
|
||||
|
||||
// --- Shared timers -----------------------------------------------------
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: root._refreshIntervalMs
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.refresh()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: actionStatusTimer
|
||||
interval: 4000
|
||||
repeat: false
|
||||
onTriggered: root.actionStatus = ""
|
||||
}
|
||||
|
||||
// --- Bar icon ------------------------------------------------------------
|
||||
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: Appearance.iconSize
|
||||
color: root.anyConnected ? Colors.primary : Colors.muted
|
||||
text: "vpn_lock"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
panel.visible = !panel.visible
|
||||
grab.active = panel.visible
|
||||
}
|
||||
}
|
||||
|
||||
// --- Reusable pieces -----------------------------------------------------
|
||||
|
||||
component ToggleSwitch: Item {
|
||||
id: toggleRoot
|
||||
property bool checked: false
|
||||
property bool busy: false
|
||||
signal toggled()
|
||||
implicitWidth: 34
|
||||
implicitHeight: 18
|
||||
opacity: toggleRoot.enabled ? 1 : 0.4
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: toggleRoot.checked ? Colors.primary : Colors.muted
|
||||
opacity: (toggleRoot.checked ? 1 : 0.35) * (toggleRoot.busy ? 0.6 : 1)
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.height - 4
|
||||
height: parent.height - 4
|
||||
radius: 0
|
||||
color: Colors.background
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: toggleRoot.checked ? parent.width - width - 2 : 2
|
||||
Behavior on x { NumberAnimation { duration: Appearance.animationDuration } }
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: !toggleRoot.busy
|
||||
cursorShape: toggleRoot.busy ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||
onClicked: toggleRoot.toggled()
|
||||
}
|
||||
}
|
||||
|
||||
// A single provider's row — icon, name, live status line, and a toggle
|
||||
// that connects/disconnects it (routed back through root so it can
|
||||
// enforce the one-provider-at-a-time rule).
|
||||
component ProviderRow: Rectangle {
|
||||
id: provRow
|
||||
property string label: ""
|
||||
property string subIcon: "shield"
|
||||
property string detailText: ""
|
||||
property string state: "disconnected"
|
||||
property bool installed: true
|
||||
signal toggle()
|
||||
width: parent ? parent.width : 0
|
||||
radius: 0
|
||||
implicitHeight: rowContent.implicitHeight + 12
|
||||
color: provRow.state === "connected"
|
||||
? Qt.rgba(Colors.primary.r, Colors.primary.g, Colors.primary.b, 0.18)
|
||||
: "transparent"
|
||||
|
||||
RowLayout {
|
||||
id: rowContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: 6
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
Layout.preferredWidth: 20
|
||||
Layout.minimumWidth: 20
|
||||
Layout.maximumWidth: 20
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: provRow.subIcon
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 14
|
||||
color: provRow.state === "connected" ? Colors.primary : Colors.foreground
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 1
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: provRow.label
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSize
|
||||
font.bold: provRow.state === "connected"
|
||||
color: Colors.foreground
|
||||
}
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: !provRow.installed ? "Not installed"
|
||||
: provRow.state === "connecting" ? "Connecting…"
|
||||
: provRow.state === "disconnecting" ? "Disconnecting…"
|
||||
: provRow.state === "connected" ? (provRow.detailText || "Connected")
|
||||
: "Disconnected"
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
}
|
||||
}
|
||||
|
||||
ToggleSwitch {
|
||||
enabled: provRow.installed
|
||||
checked: provRow.state === "connected" || provRow.state === "connecting"
|
||||
busy: provRow.state === "connecting" || provRow.state === "disconnecting"
|
||||
onToggled: provRow.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Panel -----------------------------------------------------------
|
||||
|
||||
PopupWindow {
|
||||
id: panel
|
||||
visible: false
|
||||
color: "transparent"
|
||||
readonly property int contentWidth: 244
|
||||
readonly property int contentPadding: 16
|
||||
implicitWidth: contentWidth + contentPadding * 2
|
||||
implicitHeight: mainColumn.implicitHeight + contentPadding * 2
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation { duration: Appearance.animationDuration; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
onVisibleChanged: if (visible) root.refresh()
|
||||
|
||||
anchor {
|
||||
window: root.barWindow
|
||||
rect.x: 0
|
||||
rect.y: 0
|
||||
rect.width: root.barWindow.width
|
||||
rect.height: root.barWindow.height
|
||||
edges: Edges.Bottom | Edges.Right
|
||||
gravity: Edges.Bottom | Edges.Left
|
||||
margins.bottom: -4
|
||||
margins.right: 0
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [panel]
|
||||
onCleared: panel.visible = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 0
|
||||
color: Colors.surface
|
||||
border.color: Colors.muted
|
||||
border.width: 3
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: panel.contentPadding
|
||||
spacing: 10
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: "vpn_lock"
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 22
|
||||
color: root.anyConnected ? Colors.primary : Colors.foreground
|
||||
}
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "VPN"
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: Colors.foreground
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: root.actionStatus !== ""
|
||||
width: parent.width
|
||||
text: root.actionStatus
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
|
||||
ProviderRow {
|
||||
label: "Mullvad"
|
||||
subIcon: "shield"
|
||||
state: root.mullvadState
|
||||
detailText: root.mullvadDetail
|
||||
installed: root.mullvadInstalled
|
||||
onToggle: root.toggleMullvad()
|
||||
}
|
||||
ProviderRow {
|
||||
label: "ProtonVPN"
|
||||
subIcon: "gpp_good"
|
||||
state: root.protonState
|
||||
detailText: root.protonDetail
|
||||
installed: root.protonInstalled
|
||||
onToggle: root.toggleProton()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "Connecting one disconnects the other — only one VPN tunnel at a time."
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
dotfiles/.config/quickshell/modules/bar/Workspaces.qml
Normal file
44
dotfiles/.config/quickshell/modules/bar/Workspaces.qml
Normal file
@@ -0,0 +1,44 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Hyprland
|
||||
import "../../config"
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
property var screen
|
||||
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
model: Hyprland.workspaces
|
||||
|
||||
delegate: Rectangle {
|
||||
id: pill
|
||||
required property var modelData
|
||||
visible: modelData.monitor && root.screen && modelData.monitor.name === root.screen.name
|
||||
|
||||
implicitWidth: 26
|
||||
implicitHeight: 22
|
||||
radius: Appearance.radiusSmall
|
||||
color: modelData.urgent ? Colors.urgent
|
||||
: modelData.active ? Colors.primary
|
||||
: "transparent"
|
||||
|
||||
Behavior on color { ColorAnimation { duration: Appearance.animationDuration } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.name
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: modelData.active ? Colors.primaryText : Colors.foreground
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: Hyprland.dispatch("workspace " + modelData.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
dotfiles/.config/quickshell/modules/osd/VolumeOsd.qml
Normal file
125
dotfiles/.config/quickshell/modules/osd/VolumeOsd.qml
Normal file
@@ -0,0 +1,125 @@
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.Pipewire
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "../../config"
|
||||
|
||||
PanelWindow {
|
||||
id: osd
|
||||
visible: false
|
||||
color: "transparent"
|
||||
exclusiveZone: 0
|
||||
|
||||
// Top-right, just under the bar — same fixed distance from the bar and
|
||||
// screen edge (Appearance.spacing) that every popup opened from the bar
|
||||
// itself uses, so this lines up with them instead of floating
|
||||
// bottom-center.
|
||||
anchors { top: true; right: true }
|
||||
margins.top: Appearance.spacing
|
||||
margins.right: Appearance.spacing
|
||||
|
||||
implicitWidth: 272
|
||||
implicitHeight: 54
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "quickshell:osd"
|
||||
|
||||
readonly property var sink: Pipewire.defaultAudioSink
|
||||
PwObjectTracker { objects: osd.sink ? [osd.sink] : [] }
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: 1400
|
||||
onTriggered: osd.visible = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: osd.sink ? osd.sink.audio : null
|
||||
function onVolumeChanged() { osd.visible = true; hideTimer.restart() }
|
||||
function onMutedChanged() { osd.visible = true; hideTimer.restart() }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.radius
|
||||
color: Colors.surface
|
||||
border.color: Colors.muted
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 12
|
||||
|
||||
Text {
|
||||
font.family: Appearance.iconFontFamily
|
||||
font.variableAxes: Appearance.iconFontAxes
|
||||
font.pixelSize: 18
|
||||
color: Colors.foreground
|
||||
text: {
|
||||
const audio = osd.sink ? osd.sink.audio : null
|
||||
if (!audio || audio.muted || audio.volume <= 0) return "volume_off"
|
||||
return audio.volume < 0.5 ? "volume_down" : "volume_up"
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: slider
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 16
|
||||
|
||||
readonly property real value: osd.sink && osd.sink.audio ? osd.sink.audio.volume : 0
|
||||
readonly property bool enabled: !!osd.sink
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 4
|
||||
radius: 0
|
||||
color: Colors.muted
|
||||
opacity: 0.35
|
||||
}
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
width: track.width * Math.max(0, Math.min(1, slider.value))
|
||||
height: 4
|
||||
radius: 0
|
||||
color: Colors.primary
|
||||
opacity: slider.enabled ? 1 : 0.4
|
||||
}
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 12
|
||||
radius: 6
|
||||
color: Colors.primary
|
||||
opacity: slider.enabled ? 1 : 0.4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: Math.max(0, Math.min(track.width - width, track.width * slider.value - width / 2))
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: slider.enabled
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
function updateFromX(mx) {
|
||||
if (!osd.sink || !osd.sink.audio) return
|
||||
osd.sink.audio.volume = Math.max(0, Math.min(1, mx / track.width))
|
||||
}
|
||||
onPressed: (mouse) => updateFromX(mouse.x)
|
||||
onPositionChanged: (mouse) => { if (pressed) updateFromX(mouse.x) }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.preferredWidth: 32
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.family: Appearance.fontFamily
|
||||
font.pixelSize: Appearance.fontSizeSmall
|
||||
color: Colors.muted
|
||||
text: Math.round(slider.value * 100) + "%"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
dotfiles/.config/quickshell/shell.qml
Normal file
16
dotfiles/.config/quickshell/shell.qml
Normal file
@@ -0,0 +1,16 @@
|
||||
import Quickshell
|
||||
import "./modules/bar"
|
||||
import "./modules/osd"
|
||||
|
||||
ShellRoot {
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
Bar {
|
||||
required property var modelData
|
||||
screen: modelData
|
||||
}
|
||||
}
|
||||
|
||||
VolumeOsd {}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"modules-left": ["custom/notification", "battery", "clock#date", "clock","custom/updater", "custom/launcher"],
|
||||
"modules-center": ["hyprland/workspaces"],
|
||||
"modules-right": ["network", "bluetooth", "pulseaudio", "custom/power"],
|
||||
"modules-right": ["network", "bluetooth", "wireplumber", "wireplumber#microphone", "custom/power"],
|
||||
|
||||
"sway/mode": {
|
||||
"format": "<span style=\"italic\">{}</span>"
|
||||
},
|
||||
"network": {
|
||||
"format-wifi": "<span color='#962fb5'>{icon} </span>{essid} ({signalStrength}%)",
|
||||
"format-ethernet": "<span color='#962fb5'> </span>Ethernet",
|
||||
"format-disconnected": " Not Connected",
|
||||
"format-wifi": "<span color='#962fb5'>{icon}<span letter_spacing='500'> </span></span>{essid}({signalStrength}%)",
|
||||
"format-ethernet": "<span color='#962fb5'font='bold'>{icon}<span letter_spacing='500'> </span></span><span rise='1000'>Ethernet</span>",
|
||||
"format-disconnected": "<span letter_spacing='500'> </span><span rise='1000'>Not Connected</span>",
|
||||
"tooltip-format": "{ipaddr} via {gwaddr}",
|
||||
"tooltip-format-disconnected": "Disconnected",
|
||||
"max-length": 50,
|
||||
@@ -17,7 +17,7 @@
|
||||
"on-click-right": "kitty -e nmtui",
|
||||
"format-icons": {
|
||||
"wifi": ["", "", "", "", ""],
|
||||
"ethernet": "",
|
||||
"ethernet": "",
|
||||
"disconnected": ""
|
||||
}
|
||||
},
|
||||
@@ -33,10 +33,10 @@
|
||||
"spacing": 10
|
||||
},
|
||||
"clock": {
|
||||
"format": " {:%H:%M}"
|
||||
"format": " <span rise='300'>{:%H:%M}</span>"
|
||||
},
|
||||
"clock#date": {
|
||||
"format": " {:%A, %B %d, %Y}",
|
||||
"format": " <span rise='300'>{:%A, %B %d, %Y}</span>",
|
||||
"tooltip-format": "<tt><small>{calendar}</small></tt>",
|
||||
"calendar": {
|
||||
"mode" : "year",
|
||||
@@ -55,27 +55,30 @@
|
||||
"on-click-right": "mode"
|
||||
}
|
||||
},
|
||||
"pulseaudio": {
|
||||
"format": "{icon} {volume}% {format_source}",
|
||||
"format-muted": " {format_source}",
|
||||
"format-bluetooth": "{icon} {volume}% {format_source}",
|
||||
"format-bluetooth-muted": " {format_source}",
|
||||
"format-source": " {volume}%",
|
||||
"format-source-muted": " {volume}%",
|
||||
"format-icons": {
|
||||
"default": [
|
||||
"<span color='#808080'></span>",
|
||||
"<span color='#ffff66'></span>",
|
||||
"<span color='#00ff7f'> </span>"]
|
||||
"wireplumber": {
|
||||
"format": "<span rise='300'>{icon}</span>{volume}%",
|
||||
"format-muted": "",
|
||||
"format-bluetooth": "{icon}{volume}%",
|
||||
"format-bluetooth-muted": "",
|
||||
"format-icons": [
|
||||
"<span color='#808080'><span letter_spacing='500'> </span></span>",
|
||||
"<span color='#ffff66'><span letter_spacing='500'> </span></span>",
|
||||
"<span color='#00ff7f'><span letter_spacing='3000'> </span></span>"
|
||||
],
|
||||
"on-click": "pwvucontrol"
|
||||
},
|
||||
"on-click": "pavucontrol"
|
||||
"wireplumber#microphone": {
|
||||
"node-type": "Audio/Source",
|
||||
"format": "<span letter_spacing='100'> </span><span rise='300'>{volume}%</span>",
|
||||
"format-muted": "<span letter_spacing='2000'> </span>{volume}%",
|
||||
"on-click": "pwvucontrol"
|
||||
},
|
||||
"bluetooth": {
|
||||
"format-on": "<span color='#00ff7f'></span> On",
|
||||
"format-on": "<span color='#00ff7f'><span letter_spacing='500'> </span></span>On",
|
||||
"format-off": "<span color='#888888'> Off</span>",
|
||||
"format-disabled": "<span color='#888888'> Off</span>",
|
||||
"format-connected": "<span color='#0478d6'></span> {device_alias}",
|
||||
"format-connected-battery": "<span color='#0478d6'> </span> {device_alias} <span color='#0478d6'> </span> {device_battery_percentage}%",
|
||||
"format-connected": "<span color='#0478d6'><span letter_spacing='500'> </span></span>{device_alias}",
|
||||
"format-connected-battery": "<span color='#0478d6'><span letter_spacing='500'> </span></span>{device_alias}<span letter_spacing='500'> </span><span color='#0478d6'><span letter_spacing='500'> </span></span>{device_battery_percentage}%",
|
||||
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
|
||||
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
|
||||
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
|
||||
@@ -106,11 +109,11 @@
|
||||
"warning": 30,
|
||||
"critical": 15
|
||||
},
|
||||
"format": "{icon} {capacity}%",
|
||||
"format-charging": "{icon} {capacity}%",
|
||||
"format-plugged": "{icon} {capacity}%",
|
||||
"format-alt": "{time} {icon}",
|
||||
"format-full": " {capacity}%",
|
||||
"format": "{icon}<span letter_spacing='1'> </span>{capacity}%",
|
||||
"format-charging": "{icon}<span letter_spacing='1'> </span>{capacity}%",
|
||||
"format-plugged": "{icon}<span letter_spacing='1'> </span>{capacity}%",
|
||||
"format-alt": "{time}<span letter_spacing='1'> </span>{icon}",
|
||||
"format-full": "<span letter_spacing='1'> </span>{capacity}%",
|
||||
"format-icons": {
|
||||
"default": ["", "", "", "", "", "", "", "", ""],
|
||||
"charging": ["", "", "", "", "", "", "", "", ""],
|
||||
@@ -137,13 +140,13 @@
|
||||
"sort-by-number": true
|
||||
},
|
||||
"custom/updater": {
|
||||
"format": " {}",
|
||||
"format": " <span rise='300'>{}</span>",
|
||||
"exec": "checkupdates | wc -l",
|
||||
"exec-if": "[[ $(checkupdates | wc -l) ]]",
|
||||
"interval": 5,
|
||||
"interval": 1300,
|
||||
"on-click": "$HOME/.config/waybar/scripts/update-distro.sh",
|
||||
"tooltip": true,
|
||||
"tooltip-format": "Left Click: Update System\nArch (w/ notification)\nFedora, OpenSuse, Debian/Ubuntu click to update"
|
||||
"tooltip-format": "Left Click: Update System\nRight Click: Update Package Count\nArch (w/ notification)"
|
||||
},
|
||||
|
||||
"custom/notification": {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
font-family:
|
||||
Font Awesome,
|
||||
Roboto,
|
||||
"NotoSansM Nerd Font",
|
||||
"NotoSansMono Nerd Font",
|
||||
"Font Awesome 6 Free",
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-size: 13px;
|
||||
@@ -16,10 +19,11 @@ window#waybar {
|
||||
/*-----module groups----*/
|
||||
.modules-right {
|
||||
margin: 2px 10px 0 0;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
#custom-notification {
|
||||
font-family: "NotoSansMono Nerd Font";
|
||||
font-family: "NotoSansM Nerd Font", "NotoSansMono Nerd Font";
|
||||
font-size: 13px;
|
||||
background-color: rgba(0, 43, 51, 0.85);
|
||||
border-radius: 20px;
|
||||
@@ -37,6 +41,7 @@ window#waybar {
|
||||
|
||||
.modules-left {
|
||||
margin: 2px 0 0 5px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
/*-----modules indv----*/
|
||||
#workspaces button {
|
||||
@@ -68,7 +73,8 @@ window#waybar {
|
||||
border-radius: 20px;
|
||||
margin: 5px 3px;
|
||||
}
|
||||
#pulseaudio {
|
||||
#wireplumber,
|
||||
#wireplumber-microphone {
|
||||
padding: 5px 10px 5px 15px;
|
||||
background-color: rgba(0, 43, 51, 0.85);
|
||||
border-radius: 20px;
|
||||
@@ -118,7 +124,8 @@ window#waybar {
|
||||
#idle_inhibitor.activated {
|
||||
color: #2dcc36;
|
||||
}
|
||||
#pulseaudio.muted {
|
||||
#wireplumber.muted,
|
||||
#wireplumber-microphone.muted {
|
||||
color: #cc3436;
|
||||
}
|
||||
#battery.charging {
|
||||
|
||||
@@ -25,6 +25,9 @@ source $ZSH/oh-my-zsh.sh
|
||||
|
||||
alias cl='clear'
|
||||
alias zed='zeditor'
|
||||
alias ssh-gavin='ssh thoams@192.168.1.83'
|
||||
alias ssh-raspberry-pi='ssh thoams@192.168.1.86'
|
||||
alias ssh-ted='ssh thoams@192.168.1.87'
|
||||
|
||||
# Set-up FZF key bindings (CTRL R for fuzzy history finder)
|
||||
source <(fzf --zsh)
|
||||
@@ -43,6 +46,8 @@ export LC_ALL=C.UTF-8
|
||||
|
||||
export SSH_AUTH_SOCK=$HOME/.bitwarden-ssh-agent.sock
|
||||
|
||||
export ZED_ALLOW_EMULATED_GPU=1
|
||||
|
||||
fastfetch
|
||||
|
||||
eval "$(oh-my-posh init zsh --config $HOME/.config/ohmyposh/zen.toml)"
|
||||
|
||||
Reference in New Issue
Block a user