This commit is contained in:
2026-07-05 23:16:16 +05:30
commit 8abef18b68
28 changed files with 3212 additions and 0 deletions
+355
View File
@@ -0,0 +1,355 @@
# #######################################################################################
# AUTOGENERATED HYPRLAND CONFIG.
# EDIT THIS CONFIG ACCORDING TO THE WIKI INSTRUCTIONS.
# #######################################################################################
# This is an example Hyprland config file.
# Refer to the wiki for more information.
# https://wiki.hypr.land/Configuring/
# Please note not all available settings / options are set here.
# For a full list, see the wiki
# You can split this configuration into multiple files
# Create your files separately and then link them to this file like this:
# source = ~/.config/hypr/myColors.conf
################
### MONITORS ###
################
# See https://wiki.hypr.land/Configuring/Monitors/
monitor=,preferred,auto,1
###################
### MY PROGRAMS ###
###################
# See https://wiki.hypr.land/Configuring/Keywords/
# Set programs that you use
$terminal = kitty
$fileManager = thunar
$menu = fuzzel
#################
### AUTOSTART ###
#################
# Autostart necessary processes (like notifications daemons, status bars, etc.)
# Or execute your favorite apps at launch like this:
# exec-once = $terminal
# exec-once = nm-applet &
# exec-once = waybar & hyprpaper & firefox
exec-once = mpvpaper -o "no-audio --loop" '*' ~/wallpaper.mp4
exec-once = qs
#############################
### ENVIRONMENT VARIABLES ###
#############################
# See https://wiki.hypr.land/Configuring/Environment-variables/
env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24
###################
### PERMISSIONS ###
###################
# See https://wiki.hypr.land/Configuring/Permissions/
# Please note permission changes here require a Hyprland restart and are not applied on-the-fly
# for security reasons
# ecosystem {
# enforce_permissions = 1
# }
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
#####################
### LOOK AND FEEL ###
#####################
# Refer to https://wiki.hypr.land/Configuring/Variables/
# https://wiki.hypr.land/Configuring/Variables/#general
general {
gaps_in = 0
gaps_out = 0
border_size = 0
# https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
col.inactive_border = rgba(595959aa)
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
# Please see https://wiki.hypr.land/Configuring/Tearing/ before you turn this on
allow_tearing = false
layout = dwindle
}
# https://wiki.hypr.land/Configuring/Variables/#decoration
decoration {
rounding = 0
rounding_power = 0
# Change transparency of focused and unfocused windows
active_opacity = 1.0
inactive_opacity = 1.0
shadow {
enabled = true
range = 10
render_power = 4
color = rgba(1a1a1aee)
}
# https://wiki.hypr.land/Configuring/Variables/#blur
blur {
enabled = true
size = 3
passes = 1
brightness = 0.40
vibrancy = 0.1696
}
}
# https://wiki.hypr.land/Configuring/Variables/#animations
animations {
enabled = yes, please :)
# Default curves, see https://wiki.hypr.land/Configuring/Animations/#curves
# NAME, X0, Y0, X1, Y1
bezier = easeOutQuint, 0.23, 1, 0.32, 1
bezier = easeInOutCubic, 0.65, 0.05, 0.36, 1
bezier = linear, 0, 0, 1, 1
bezier = almostLinear, 0.5, 0.5, 0.75, 1
bezier = quick, 0.15, 0, 0.1, 1
# Default animations, see https://wiki.hypr.land/Configuring/Animations/
# NAME, ONOFF, SPEED, CURVE, [STYLE]
animation = global, 1, 10, default
animation = border, 1, 5.39, easeOutQuint
animation = windows, 1, 4.79, easeOutQuint
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
animation = windowsOut, 1, 1.49, linear, popin 87%
animation = fadeIn, 1, 1.73, almostLinear
animation = fadeOut, 1, 1.46, almostLinear
animation = fade, 1, 3.03, quick
animation = layers, 1, 3.81, easeOutQuint
animation = layersIn, 1, 4, easeOutQuint, fade
animation = layersOut, 1, 1.5, linear, fade
animation = fadeLayersIn, 1, 1.79, almostLinear
animation = fadeLayersOut, 1, 1.39, almostLinear
animation = workspaces, 1, 1.94, almostLinear, fade
animation = workspacesIn, 1, 1.21, almostLinear, fade
animation = workspacesOut, 1, 1.94, almostLinear, fade
animation = zoomFactor, 1, 7, quick
}
# Ref https://wiki.hypr.land/Configuring/Workspace-Rules/
# "Smart gaps" / "No gaps when only"
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
# windowrule {
# name = no-gaps-wtv1
# match:float = false
# match:workspace = w[tv1]
#
# border_size = 0
# rounding = 0
# }
#
# windowrule {
# name = no-gaps-f1
# match:float = false
# match:workspace = f[1]
#
# border_size = 0
# rounding = 0
# }
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
dwindle {
preserve_split = true # You probably want this
}
# See https://wiki.hypr.land/Configuring/Master-Layout/ for more
master {
new_status = master
}
# https://wiki.hypr.land/Configuring/Variables/#misc
misc {
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
}
#############
### INPUT ###
#############
# https://wiki.hypr.land/Configuring/Variables/#input
input {
kb_layout = us
kb_variant =
kb_model =
kb_options =
kb_rules =
follow_mouse = 1
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
touchpad {
natural_scroll = false
}
}
# See https://wiki.hypr.land/Configuring/Gestures
gesture = 3, horizontal, workspace
# Example per-device config
# See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more
device {
name = epic-mouse-v1
sensitivity = -0.5
}
###################
### KEYBINDINGS ###
###################
# See https://wiki.hypr.land/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier
# Screenshot binds
bind = , Print, exec, grim -g "$(slurp)" - | swappy -f -
bind = $mainMod, Print, exec, grim -g "$(slurp -d)" - | wl-copy
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
bind = $mainMod, Q, exec, $terminal
bind = $mainMod, C, killactive,
bind = $mainMod, M, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit
bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating,
bind = $mainMod, R, exec, $menu
bind = $mainMod, P, pseudo, # dwindle
bind = $mainMod, J, layoutmsg, togglesplit # dwindle
# Move focus with mainMod + arrow keys
bind = $mainMod, left, movefocus, l
bind = $mainMod, right, movefocus, r
bind = $mainMod, up, movefocus, u
bind = $mainMod, down, movefocus, d
# Switch workspaces with mainMod + [0-9]
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
bind = $mainMod, 3, workspace, 3
bind = $mainMod, 4, workspace, 4
bind = $mainMod, 5, workspace, 5
bind = $mainMod, 6, workspace, 6
bind = $mainMod, 7, workspace, 7
bind = $mainMod, 8, workspace, 8
bind = $mainMod, 9, workspace, 9
bind = $mainMod, 0, workspace, 10
# Move active window to a workspace with mainMod + SHIFT + [0-9]
bind = $mainMod SHIFT, 1, movetoworkspace, 1
bind = $mainMod SHIFT, 2, movetoworkspace, 2
bind = $mainMod SHIFT, 3, movetoworkspace, 3
bind = $mainMod SHIFT, 4, movetoworkspace, 4
bind = $mainMod SHIFT, 5, movetoworkspace, 5
bind = $mainMod SHIFT, 6, movetoworkspace, 6
bind = $mainMod SHIFT, 7, movetoworkspace, 7
bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace, magic
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
# Custom bind for fanboost
bind = , XF86Launch2, exec, sudo /usr/local/bin/fanboost.sh
# Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+
bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-
# Requires playerctl
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPause, exec, playerctl play-pause
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioPrev, exec, playerctl previous
##############################
### WINDOWS AND WORKSPACES ###
##############################
# See https://wiki.hypr.land/Configuring/Window-Rules/ for more
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
# Example windowrules that are useful
windowrule {
# Ignore maximize requests from all apps. You'll probably like this.
name = suppress-maximize-events
match:class = .*
suppress_event = maximize
}
windowrule {
# Fix some dragging issues with XWayland
name = fix-xwayland-drags
match:class = ^$
match:title = ^$
match:xwayland = true
match:float = true
match:fullscreen = false
match:pin = false
no_focus = true
}
# Hyprland-run windowrule
windowrule {
name = move-hyprland-run
match:class = hyprland-run
move = 20 monitor_h-120
float = yes
}
+367
View File
@@ -0,0 +1,367 @@
------------------
---- MONITORS ----
------------------
-- See https://wiki.hypr.land/Configuring/Basics/Monitors/
hl.monitor({
output = "",
mode = "preferred",
position = "auto",
scale = "1",
})
---------------------
---- MY PROGRAMS ----
---------------------
-- Set programs that you use
local terminal = "kitty"
local fileManager = "thunar"
local menu = "fuzzel"
-------------------
---- AUTOSTART ----
-------------------
-- See https://wiki.hypr.land/Configuring/Basics/Autostart/
-- Autostart necessary processes (like notifications daemons, status bars, etc.)
-- Or execute your favorite apps at launch like this:
--
hl.on("hyprland.start", function()
hl.exec_cmd("prime-run mpvpaper -o \"no-audio --loop\" '*' ~/wallpaper.mp4")
hl.exec_cmd("qs")
hl.exec_cmd("kitty --class sticky-notes sh -c 'while true; do nvim ~/notes.md; sleep 0.3; done'")
end)
-------------------------------
---- ENVIRONMENT VARIABLES ----
-------------------------------
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Environment-variables/
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
-----------------------
----- PERMISSIONS -----
-----------------------
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Permissions/
-- Please note permission changes here require a Hyprland restart and are not applied on-the-fly
-- for security reasons
-- hl.config({
-- ecosystem = {
-- enforce_permissions = true,
-- },
-- })
-- hl.permission("/usr/(bin|local/bin)/grim", "screencopy", "allow")
-- hl.permission("/usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland", "screencopy", "allow")
-- hl.permission("/usr/(bin|local/bin)/hyprpm", "plugin", "allow")
-----------------------
---- LOOK AND FEEL ----
-----------------------
-- Refer to https://wiki.hypr.land/Configuring/Basics/Variables/
hl.config({
general = {
gaps_in = 0,
gaps_out = 0,
border_size = 0,
col = {
active_border = { colors = { "rgba(33ccffee)", "rgba(00ff99ee)" }, angle = 45 },
inactive_border = "rgba(595959aa)",
},
-- Set to true to enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = true,
-- Please see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Tearing/ before you turn this on
allow_tearing = false,
layout = "dwindle",
},
decoration = {
rounding = 0,
rounding_power = 0,
-- Change transparency of focused and unfocused windows
active_opacity = 1.0,
inactive_opacity = 1.0,
shadow = {
enabled = true,
range = 4,
render_power = 3,
color = 0xee1a1a1a,
},
blur = {
enabled = true,
size = 3,
passes = 1,
vibrancy = 0.1696,
},
},
animations = {
enabled = true,
},
})
-- Default curves and animations, see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Animations/
hl.curve("easeOutQuint", { type = "bezier", points = { { 0.23, 1 }, { 0.32, 1 } } })
hl.curve("easeInOutCubic", { type = "bezier", points = { { 0.65, 0.05 }, { 0.36, 1 } } })
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
hl.curve("almostLinear", { type = "bezier", points = { { 0.5, 0.5 }, { 0.75, 1 } } })
hl.curve("quick", { type = "bezier", points = { { 0.15, 0 }, { 0.1, 1 } } })
-- Default springs
hl.curve("easy", { type = "spring", mass = 1, stiffness = 71.2633, dampening = 15.8273644 })
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, spring = "easy" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, spring = "easy", style = "popin 87%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
hl.animation({ leaf = "fade", enabled = true, speed = 3.03, bezier = "quick" })
hl.animation({ leaf = "layers", enabled = true, speed = 3.81, bezier = "easeOutQuint" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 4, bezier = "easeOutQuint", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 1.5, bezier = "linear", style = "fade" })
hl.animation({ leaf = "fadeLayersIn", enabled = true, speed = 1.79, bezier = "almostLinear" })
hl.animation({ leaf = "fadeLayersOut", enabled = true, speed = 1.39, bezier = "almostLinear" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 1.21, bezier = "almostLinear", style = "fade" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 1.94, bezier = "almostLinear", style = "fade" })
hl.animation({ leaf = "zoomFactor", enabled = true, speed = 7, bezier = "quick" })
-- Ref https://wiki.hypr.land/Configuring/Basics/Workspace-Rules/
-- "Smart gaps" / "No gaps when only"
-- uncomment all if you wish to use that.
-- hl.workspace_rule({ workspace = "w[tv1]", gaps_out = 0, gaps_in = 0 })
-- hl.workspace_rule({ workspace = "f[1]", gaps_out = 0, gaps_in = 0 })
-- hl.window_rule({
-- name = "no-gaps-wtv1",
-- match = { float = false, workspace = "w[tv1]" },
-- border_size = 0,
-- rounding = 0,
-- })
-- hl.window_rule({
-- name = "no-gaps-f1",
-- match = { float = false, workspace = "f[1]" },
-- border_size = 0,
-- rounding = 0,
-- })
-- See https://wiki.hypr.land/Configuring/Layouts/Dwindle-Layout/ for more
hl.config({
dwindle = {
preserve_split = true, -- You probably want this
},
})
-- See https://wiki.hypr.land/Configuring/Layouts/Master-Layout/ for more
hl.config({
master = {
new_status = "master",
},
})
-- See https://wiki.hypr.land/Configuring/Layouts/Scrolling-Layout/ for more
hl.config({
scrolling = {
fullscreen_on_one_column = true,
},
})
----------------
---- MISC ----
----------------
hl.config({
misc = {
force_default_wallpaper = -1, -- Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = false, -- If true disables the random hyprland logo / anime girl background. :(
},
})
---------------
---- INPUT ----
---------------
hl.config({
input = {
kb_layout = "us",
kb_variant = "",
kb_model = "",
kb_options = "",
kb_rules = "",
follow_mouse = 1,
sensitivity = 0, -- -1.0 - 1.0, 0 means no modification.
touchpad = {
natural_scroll = true,
},
},
})
hl.gesture({
fingers = 3,
direction = "horizontal",
action = "workspace"
})
-- Example per-device config
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Devices/ for more
hl.device({
name = "epic-mouse-v1",
sensitivity = -0.5,
})
---------------------
---- KEYBINDINGS ----
---------------------
local mainMod = "SUPER" -- Sets "Windows" key as main modifier
-- Example binds, see https://wiki.hypr.land/Configuring/Basics/Binds/ for more
hl.bind("Print", hl.dsp.exec_cmd("grim -g \"$(slurp)\" - | wl-copy"))
hl.bind(mainMod .. " + Q", hl.dsp.exec_cmd(terminal))
local closeWindowBind = hl.bind(mainMod .. " + C", hl.dsp.window.close())
-- closeWindowBind:set_enabled(false)
hl.bind(mainMod .. " + M",
hl.dsp.exec_cmd("command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch 'hl.dsp.exit()'"))
hl.bind(mainMod .. " + E", hl.dsp.exec_cmd(fileManager))
hl.bind(mainMod .. " + V", hl.dsp.window.float({ action = "toggle" }))
hl.bind(mainMod .. " + R", hl.dsp.exec_cmd(menu))
hl.bind(mainMod .. " + P", hl.dsp.window.pseudo())
hl.bind(mainMod .. " + J", hl.dsp.layout("togglesplit")) -- dwindle only
-- Move focus with mainMod + arrow keys
hl.bind(mainMod .. " + left", hl.dsp.focus({ direction = "left" }))
hl.bind(mainMod .. " + right", hl.dsp.focus({ direction = "right" }))
hl.bind(mainMod .. " + up", hl.dsp.focus({ direction = "up" }))
hl.bind(mainMod .. " + down", hl.dsp.focus({ direction = "down" }))
-- Switch workspaces with mainMod + [0-9]
-- Move active window to a workspace with mainMod + SHIFT + [0-9]
for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0
hl.bind(mainMod .. " + " .. key, hl.dsp.focus({ workspace = i }))
hl.bind(mainMod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- Example special workspace (scratchpad)
hl.bind(mainMod .. " + S", hl.dsp.workspace.toggle_special("magic"))
hl.bind(mainMod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special:magic" }))
-- Sticky notes special workspace
hl.bind(mainMod .. " + grave", hl.dsp.workspace.toggle_special("notes"))
hl.bind(mainMod .. " + SHIFT + grave", hl.dsp.window.move({ workspace = "special:notes" }))
-- Scroll through existing workspaces with mainMod + scroll
hl.bind(mainMod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
hl.bind(mainMod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
-- Move/resize windows with mainMod + LMB/RMB and dragging
hl.bind(mainMod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mainMod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- Laptop multimedia keys for volume and LCD brightness
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"),
{ locked = true, repeating = true })
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"),
{ locked = true, repeating = true })
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"),
{ locked = true, repeating = true })
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"),
{ locked = true, repeating = true })
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%+"), { locked = true, repeating = true })
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl -e4 -n2 set 5%-"), { locked = true, repeating = true })
-- Requires playerctl
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), { locked = true })
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), { locked = true })
--------------------------------
---- WINDOWS AND WORKSPACES ----
--------------------------------
-- See https://wiki.hypr.land/Configuring/Basics/Window-Rules/
-- and https://wiki.hypr.land/Configuring/Basics/Workspace-Rules/
-- Example window rules that are useful
local suppressMaximizeRule = hl.window_rule({
-- Ignore maximize requests from all apps. You'll probably like this.
name = "suppress-maximize-events",
match = { class = ".*" },
suppress_event = "maximize",
})
-- suppressMaximizeRule:set_enabled(false)
hl.window_rule({
-- Fix some dragging issues with XWayland
name = "fix-xwayland-drags",
match = {
class = "^$",
title = "^$",
xwayland = true,
float = true,
fullscreen = false,
pin = false,
},
no_focus = true,
})
-- Layer rules also return a handle.
-- local overlayLayerRule = hl.layer_rule({
-- name = "no-anim-overlay",
-- match = { namespace = "^my-overlay$" },
-- no_anim = true,
-- })
-- overlayLayerRule:set_enabled(false)
-- Hyprland-run windowrule
hl.window_rule({
name = "move-hyprland-run",
match = { class = "hyprland-run" },
move = "20 monitor_h-120",
float = true,
})
-- Sticky notes: floating, centered note pinned to special:notes workspace
hl.window_rule({
name = "sticky-notes-workspace",
match = { class = "sticky-notes" },
workspace = "special:notes",
float = true,
size = "640 720",
center = true,
})
+116
View File
@@ -0,0 +1,116 @@
local terminal = "kitty"
local fileManager = "thunar"
local menu = "fuzzel"
local mainMod = "SUPER"
-- MONITORS
hl.monitor({
output = "",
mode = "preferred",
position = "auto",
scale = 1,
})
-- AUTOSTART
hl.on("hyprland.start", function()
hl.exec_cmd('mpvpaper -o "no-audio --loop" "*" ~/wallpaper.mp4')
hl.exec_cmd("qs")
end)
-- ENV
hl.env("XCURSOR_SIZE", "24")
hl.env("HYPRCURSOR_SIZE", "24")
-- CONFIG
hl.config({
general = {
gaps_in = 0,
gaps_out = 0,
border_size = 0,
resize_on_border = false,
allow_tearing = false,
layout = "dwindle",
col = {
active_border = {
colors = { "rgba(33ccffee)", "rgba(00ff99ee)" },
angle = 45,
},
inactive_border = "rgba(595959aa)",
},
},
decoration = {
rounding = 0,
rounding_power = 0,
active_opacity = 1.0,
inactive_opacity = 1.0,
shadow = {
enabled = true,
range = 10,
render_power = 4,
color = "rgba(1a1a1aee)"
},
blur = {
enabled = true,
size = 3,
passes = 1,
brightness = 0.40,
vibrancy = 0.1696
}
},
dwindle = {
preserve_split = true
},
master = {
new_status = "master"
},
input = {
kb_layout = "us",
follow_mouse = 1,
sensitivity = 0,
touchpad = {
natural_scroll = false
}
},
misc = {
force_default_wallpaper = -1,
disable_hyprland_logo = false
}
})
-- GESTURES
-- hl.raw("gesture = 3, horizontal, workspace")
-- KEYBINDS
hl.bind(mainMod .. " + Q", hl.action.exec(terminal))
hl.bind(mainMod .. " + E", hl.action.exec(fileManager))
hl.bind(mainMod .. " + R", hl.action.exec(menu))
hl.bind(mainMod .. " + C", hl.dsp.window.close())
hl.bind(mainMod .. " + V", hl.dsp.window.toggle_floating())
for i = 1, 10 do
local ws = tostring(i == 10 and 0 or i)
local target = tostring(i)
hl.bind(mainMod .. " + " .. ws,
hl.action.dispatch("workspace " .. target))
hl.bind(mainMod .. " + SHIFT + " .. ws,
hl.action.dispatch("movetoworkspace " .. target))
end
-- MEDIA KEYS
hl.raw('bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+')
hl.raw('bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-')
hl.raw('bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle')
-- WINDOW RULES
hl.raw('windowrule = suppressevent maximize,class:.*')
+8
View File
@@ -0,0 +1,8 @@
tt.*
.tests
doc/tags
debug
.repro
foo.*
*.log
data
+15
View File
@@ -0,0 +1,15 @@
{
"neodev": {
"library": {
"enabled": true,
"plugins": true
}
},
"neoconf": {
"plugins": {
"lua_ls": {
"enabled": true
}
}
}
}
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+4
View File
@@ -0,0 +1,4 @@
# 💤 LazyVim
A starter template for [LazyVim](https://github.com/LazyVim/LazyVim).
Refer to the [documentation](https://lazyvim.github.io/installation) to get started.
+2
View File
@@ -0,0 +1,2 @@
-- bootstrap lazy.nvim, LazyVim and your plugins
require("config.lazy")
+45
View File
@@ -0,0 +1,45 @@
{
"LazyVim": { "branch": "main", "commit": "c10948c50b18fae7f256433afdef09e432410480" },
"SchemaStore.nvim": { "branch": "main", "commit": "6ff1f21b2e2b77ec59f7433ce2d9fbc052d908ac" },
"blink-copilot": { "branch": "main", "commit": "7ad8209b2f880a2840c94cdcd80ab4dc511d4f39" },
"blink.cmp": { "branch": "main", "commit": "78336bc89ee5365633bcf754d93df01678b5c08f" },
"bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" },
"catppuccin": { "branch": "main", "commit": "e068ab5f8261f23f6f71ffd8791ae40315b77b9c" },
"conform.nvim": { "branch": "master", "commit": "619363c30309d29ffa631e67c8183f2a72caa373" },
"copilot.lua": { "branch": "master", "commit": "d521d3959f81f4fa58870ed329754f1dd5057511" },
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
"gitsigns.nvim": { "branch": "main", "commit": "eb60cc7b94c46005237fd34170d76f3a089a90aa" },
"grug-far.nvim": { "branch": "main", "commit": "c69859c1d5427ab5fc7ed12380ab521b4e336691" },
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
"lazydev.nvim": { "branch": "main", "commit": "ff2cbcba459b637ec3fd165a2be59b7bbaeedf0d" },
"lualine.nvim": { "branch": "master", "commit": "221ce6b2d999187044529f49da6554a92f740a96" },
"markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "47059d71b42d74b0a1e9f61c1d99d301039c3b5b" },
"mason.nvim": { "branch": "main", "commit": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d" },
"mini.ai": { "branch": "main", "commit": "cb20f298ebf5ae91924cd0c6c310712de2ef4086" },
"mini.icons": { "branch": "main", "commit": "24dbea2195c477e57d581215839a6ab915f34b14" },
"mini.pairs": { "branch": "main", "commit": "fd150ac39b78e6a2286f5138e472b7dc7eba43b9" },
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
"nvim-jdtls": { "branch": "master", "commit": "6e9d953f0b82bccdb834cfde0e893f3119c22592" },
"nvim-lint": { "branch": "master", "commit": "a219b2c9e5b4765e5c845aba119dad55806fcaf1" },
"nvim-lspconfig": { "branch": "master", "commit": "d224a1920728ba129880efc700d4a0180ac4ecbb" },
"nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "851e865342e5a4cb1ae23d31caf6e991e1c99f1e" },
"nvim-ts-autotag": { "branch": "main", "commit": "88c1453db4ba7dd24131086fe51fdf74e587d275" },
"persistence.nvim": { "branch": "main", "commit": "b20b2a7887bd39c1a356980b45e03250f3dce49c" },
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
"render-markdown.nvim": { "branch": "main", "commit": "f422cb5c6855f150e2ddcfaf44e7157b98b34f6a" },
"snacks.nvim": { "branch": "main", "commit": "882c996cf28183f4d63640de0b4c02ec886d01f2" },
"todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" },
"tokyonight.nvim": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" },
"trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" },
"ts-comments.nvim": { "branch": "main", "commit": "a59d6092213447450191122c9346f309161504cb" },
"venv-selector.nvim": { "branch": "main", "commit": "cc4bb3975de8835291f9bb45889e96c6b2795fc4" },
"vim-dadbod": { "branch": "master", "commit": "6d1d41da4873a445c5605f2005ad2c68c99d8770" },
"vim-dadbod-completion": { "branch": "master", "commit": "a8dac0b3cf6132c80dc9b18bef36d4cf7a9e1fe6" },
"vim-dadbod-ui": { "branch": "master", "commit": "afd07819d8efcefc3317205b855ad4e3513b0011" },
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" },
"yanky.nvim": { "branch": "main", "commit": "020c22d0cfa60358569e49d1d7b91630f175aef0" }
}
+26
View File
@@ -0,0 +1,26 @@
{
"extras": [
"lazyvim.plugins.extras.ai.copilot",
"lazyvim.plugins.extras.coding.yanky",
"lazyvim.plugins.extras.formatting.prettier",
"lazyvim.plugins.extras.lang.dart",
"lazyvim.plugins.extras.lang.docker",
"lazyvim.plugins.extras.lang.git",
"lazyvim.plugins.extras.lang.java",
"lazyvim.plugins.extras.lang.json",
"lazyvim.plugins.extras.lang.kotlin",
"lazyvim.plugins.extras.lang.markdown",
"lazyvim.plugins.extras.lang.prisma",
"lazyvim.plugins.extras.lang.python",
"lazyvim.plugins.extras.lang.sql",
"lazyvim.plugins.extras.lang.tailwind",
"lazyvim.plugins.extras.lang.toml",
"lazyvim.plugins.extras.lang.typescript",
"lazyvim.plugins.extras.util.dot"
],
"install_version": 8,
"news": {
"NEWS.md": "11866"
},
"version": 8
}
+8
View File
@@ -0,0 +1,8 @@
-- Autocmds are automatically loaded on the VeryLazy event
-- Default autocmds that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/autocmds.lua
--
-- Add any additional autocmds here
-- with `vim.api.nvim_create_autocmd`
--
-- Or remove existing autocmds by their group name (which is prefixed with `lazyvim_` for the defaults)
-- e.g. vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell")
+3
View File
@@ -0,0 +1,3 @@
-- Keymaps are automatically loaded on the VeryLazy event
-- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua
-- Add any additional keymaps here
+53
View File
@@ -0,0 +1,53 @@
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
require("lazy").setup({
spec = {
-- add LazyVim and import its plugins
{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
-- import/override with your plugins
{ import = "plugins" },
},
defaults = {
-- By default, only LazyVim plugins will be lazy-loaded. Your custom plugins will load during startup.
-- If you know what you're doing, you can set this to `true` to have all your custom plugins lazy-loaded by default.
lazy = false,
-- It's recommended to leave version=false for now, since a lot the plugin that support versioning,
-- have outdated releases, which may break your Neovim install.
version = false, -- always use the latest git commit
-- version = "*", -- try installing the latest stable version for plugins that support semver
},
install = { colorscheme = { "tokyonight", "habamax" } },
checker = {
enabled = true, -- check for plugin updates periodically
notify = false, -- notify on update
}, -- automatically check for plugin updates
performance = {
rtp = {
-- disable some rtp plugins
disabled_plugins = {
"gzip",
-- "matchit",
-- "matchparen",
-- "netrwPlugin",
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},
})
+3
View File
@@ -0,0 +1,3 @@
-- Options are automatically loaded before lazy.nvim startup
-- Default options that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/options.lua
-- Add any additional options here
+197
View File
@@ -0,0 +1,197 @@
-- since this is just an example spec, don't actually load anything here and return an empty spec
-- stylua: ignore
if true then return {} end
-- every spec file under the "plugins" directory will be loaded automatically by lazy.nvim
--
-- In your plugin files, you can:
-- * add extra plugins
-- * disable/enabled LazyVim plugins
-- * override the configuration of LazyVim plugins
return {
-- add gruvbox
{ "ellisonleao/gruvbox.nvim" },
-- Configure LazyVim to load gruvbox
{
"LazyVim/LazyVim",
opts = {
colorscheme = "gruvbox",
},
},
-- change trouble config
{
"folke/trouble.nvim",
-- opts will be merged with the parent spec
opts = { use_diagnostic_signs = true },
},
-- disable trouble
{ "folke/trouble.nvim", enabled = false },
-- override nvim-cmp and add cmp-emoji
{
"hrsh7th/nvim-cmp",
dependencies = { "hrsh7th/cmp-emoji" },
---@param opts cmp.ConfigSchema
opts = function(_, opts)
table.insert(opts.sources, { name = "emoji" })
end,
},
-- change some telescope options and a keymap to browse plugin files
{
"nvim-telescope/telescope.nvim",
keys = {
-- add a keymap to browse plugin files
-- stylua: ignore
{
"<leader>fp",
function() require("telescope.builtin").find_files({ cwd = require("lazy.core.config").options.root }) end,
desc = "Find Plugin File",
},
},
-- change some options
opts = {
defaults = {
layout_strategy = "horizontal",
layout_config = { prompt_position = "top" },
sorting_strategy = "ascending",
winblend = 0,
},
},
},
-- add pyright to lspconfig
{
"neovim/nvim-lspconfig",
---@class PluginLspOpts
opts = {
---@type lspconfig.options
servers = {
-- pyright will be automatically installed with mason and loaded with lspconfig
pyright = {},
},
},
},
-- add tsserver and setup with typescript.nvim instead of lspconfig
{
"neovim/nvim-lspconfig",
dependencies = {
"jose-elias-alvarez/typescript.nvim",
init = function()
require("lazyvim.util").lsp.on_attach(function(_, buffer)
-- stylua: ignore
vim.keymap.set( "n", "<leader>co", "TypescriptOrganizeImports", { buffer = buffer, desc = "Organize Imports" })
vim.keymap.set("n", "<leader>cR", "TypescriptRenameFile", { desc = "Rename File", buffer = buffer })
end)
end,
},
---@class PluginLspOpts
opts = {
---@type lspconfig.options
servers = {
-- tsserver will be automatically installed with mason and loaded with lspconfig
tsserver = {},
},
-- you can do any additional lsp server setup here
-- return true if you don't want this server to be setup with lspconfig
---@type table<string, fun(server:string, opts:_.lspconfig.options):boolean?>
setup = {
-- example to setup with typescript.nvim
tsserver = function(_, opts)
require("typescript").setup({ server = opts })
return true
end,
-- Specify * to use this function as a fallback for any server
-- ["*"] = function(server, opts) end,
},
},
},
-- for typescript, LazyVim also includes extra specs to properly setup lspconfig,
-- treesitter, mason and typescript.nvim. So instead of the above, you can use:
{ import = "lazyvim.plugins.extras.lang.typescript" },
-- add more treesitter parsers
{
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = {
"bash",
"html",
"javascript",
"json",
"lua",
"markdown",
"markdown_inline",
"python",
"query",
"regex",
"tsx",
"typescript",
"vim",
"yaml",
},
},
},
-- since `vim.tbl_deep_extend`, can only merge tables and not lists, the code above
-- would overwrite `ensure_installed` with the new value.
-- If you'd rather extend the default config, use the code below instead:
{
"nvim-treesitter/nvim-treesitter",
opts = function(_, opts)
-- add tsx and treesitter
vim.list_extend(opts.ensure_installed, {
"tsx",
"typescript",
})
end,
},
-- the opts function can also be used to change the default opts:
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = function(_, opts)
table.insert(opts.sections.lualine_x, {
function()
return "😄"
end,
})
end,
},
-- or you can return new options to override all the defaults
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = function()
return {
--[[add your custom lualine config here]]
}
end,
},
-- use mini.starter instead of alpha
{ import = "lazyvim.plugins.extras.ui.mini-starter" },
-- add jsonls and schemastore packages, and setup treesitter for json, json5 and jsonc
{ import = "lazyvim.plugins.extras.lang.json" },
-- add any tools you want to have installed below
{
"williamboman/mason.nvim",
opts = {
ensure_installed = {
"stylua",
"shellcheck",
"shfmt",
"flake8",
},
},
},
}
+3
View File
@@ -0,0 +1,3 @@
indent_type = "Spaces"
indent_width = 2
column_width = 120
+58
View File
@@ -0,0 +1,58 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
import "./"
LazyLoader {
required property bool visible
active: visible
ShellRoot {
PanelWindow {
// match the system theme background color
color: contentItem.palette.active.window
ScrollView {
anchors.fill: parent
contentWidth: availableWidth
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
// get a list of nodes that output to the default sink
PwNodeLinkTracker {
id: linkTracker
node: Pipewire.defaultAudioSink
}
MixerEntry {
node: Pipewire.defaultAudioSink
}
Rectangle {
Layout.fillWidth: true
color: palette.active.text
implicitHeight: 1
}
Repeater {
model: linkTracker.linkGroups
MixerEntry {
required property PwLinkGroup modelData
// Each link group contains a source and a target.
// Since the target is the default sink, we want the source.
node: modelData.source
}
}
}
}
}
}
}
+72
View File
@@ -0,0 +1,72 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import "./components"
PanelWindow {
id: root
// WlrLayershell.layer: WlrLayer.Bottom
Component.onCompleted: {
if (this.WlrLayershell != null) {
this.WlrLayershell.layer = WlrLayer.Bottom;
}
}
anchors {
top: true
left: true
right: true
}
implicitHeight: 30
color: '#28000000'
// Shared popup manager: only one click-popup (network / bluetooth / clock)
// may be open at a time. Setting `current` to a name opens that popup and
// implicitly closes any other.
QtObject {
id: popups
property string current: ""
function toggle(name: string): void {
current = current === name ? "" : name;
}
}
RowLayout {
spacing: 12
anchors {
fill: parent
leftMargin: 16
rightMargin: 16
}
Desktop {}
Rectangle {
Layout.fillWidth: true
}
NetworkControl {
popups: popups
}
BluetoothControl {
popups: popups
}
Battery {}
Clock {
popups: popups
}
}
}
+135
View File
@@ -0,0 +1,135 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
Scope {
id: root
// Backlight device under /sys/class/backlight, auto-detected at startup.
property string device: ""
// Current brightness as a 0..1 fraction of max_brightness.
property real brightness: 0
// Suppresses the OSD for the very first reading (startup, not a real change).
property bool initialised: false
property bool shouldShowOsd: false
// Nerd Font glyph matching the brightness level (matches config icon style).
function brightnessGlyph(): string {
return root.brightness < 0.5 ? "󰃞" : "󰃠";
}
// Pick the first backlight device present on the machine.
Process {
running: true
command: ["sh", "-c", "ls -1 /sys/class/backlight/ | head -n1"]
stdout: StdioCollector {
onStreamFinished: root.device = text.trim()
}
}
// max_brightness is static; a single blocking read is fine.
FileView {
id: maxBrightness
path: root.device ? `/sys/class/backlight/${root.device}/max_brightness` : ""
blockLoading: true
printErrors: false
onLoaded: root.updateBrightness()
}
// brightness changes whenever brightnessctl (Hyprland keybinds) writes it.
// inotify fires on sysfs writes, so watchChanges is enough - no polling.
FileView {
id: curBrightness
path: root.device ? `/sys/class/backlight/${root.device}/brightness` : ""
watchChanges: true
printErrors: false
onFileChanged: reload()
onLoaded: root.updateBrightness()
}
function updateBrightness(): void {
const max = parseInt(maxBrightness.text());
const cur = parseInt(curBrightness.text());
if (isNaN(max) || isNaN(cur) || max <= 0)
return;
const value = Math.max(0, Math.min(1, cur / max));
const changed = Math.abs(value - root.brightness) > 0.0001;
root.brightness = value;
if (root.initialised && changed) {
root.shouldShowOsd = true;
hideTimer.restart();
}
root.initialised = true;
}
Timer {
id: hideTimer
interval: 1000
onTriggered: root.shouldShowOsd = false
}
LazyLoader {
active: root.shouldShowOsd
PanelWindow {
anchors.bottom: true
margins.bottom: screen.height / 5
exclusiveZone: 0
implicitWidth: 400
implicitHeight: 50
color: "transparent"
// An empty click mask prevents the window from blocking mouse events.
mask: Region {}
Rectangle {
anchors.fill: parent
radius: height / 2
color: "#80000000"
RowLayout {
anchors {
fill: parent
leftMargin: 10
rightMargin: 15
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 24
color: "white"
text: root.brightnessGlyph()
}
Rectangle {
// Stretches to fill all left-over space
Layout.fillWidth: true
implicitHeight: 10
radius: 20
color: "#50ffffff"
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
color: "white"
implicitWidth: parent.width * root.brightness
radius: parent.radius
}
}
}
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell.Services.Pipewire
ColumnLayout {
required property PwNode node
// bind the node so we can read its properties
PwObjectTracker {
objects: [node]
}
RowLayout {
Image {
visible: source != ""
source: {
const icon = node.properties["application.icon-name"] ?? "audio-volume-high-symbolic";
return `image://icon/${icon}`;
}
sourceSize.width: 20
sourceSize.height: 20
}
Label {
text: {
// application.name -> description -> name
const app = node.properties["application.name"] ?? (node.description != "" ? node.description : node.name);
const media = node.properties["media.name"];
return media != undefined ? `${app} - ${media}` : app;
}
}
Button {
text: node.audio.muted ? "unmute" : "mute"
onClicked: node.audio.muted = !node.audio.muted
}
}
RowLayout {
Label {
Layout.preferredWidth: 50
text: `${Math.floor(node.audio.volume * 100)}%`
}
Slider {
Layout.fillWidth: true
value: node.audio.volume
onValueChanged: node.audio.volume = value
}
}
}
+121
View File
@@ -0,0 +1,121 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
Scope {
id: root
// Pick a Nerd Font glyph matching the current level / mute state, matching
// the glyph-based icon style used elsewhere in this config (e.g. Battery).
function volumeGlyph(): string {
const sink = Pipewire.defaultAudioSink;
if (!sink || !sink.audio || sink.audio.muted)
return "󰝟"; // muted
const v = sink.audio.volume;
if (v <= 0.0)
return "󰝟"; // muted / zero
if (v < 0.34)
return "󰕿"; // low
if (v < 0.67)
return "󰖀"; // medium
return "󰕾"; // high
}
// Bind the pipewire node so its volume will be tracked
PwObjectTracker {
objects: [Pipewire.defaultAudioSink]
}
Connections {
target: Pipewire.defaultAudioSink?.audio
function onVolumesChanged() {
root.shouldShowOsd = true;
hideTimer.restart();
}
function onMutedChanged() {
root.shouldShowOsd = true;
hideTimer.restart();
}
}
property bool shouldShowOsd: false
Timer {
id: hideTimer
interval: 1000
onTriggered: root.shouldShowOsd = false
}
// The OSD window will be created and destroyed based on shouldShowOsd.
// PanelWindow.visible could be set instead of using a loader, but using
// a loader will reduce the memory overhead when the window isn't open.
LazyLoader {
active: root.shouldShowOsd
PanelWindow {
// Since the panel's screen is unset, it will be picked by the compositor
// when the window is created. Most compositors pick the current active monitor.
anchors.bottom: true
margins.bottom: screen.height / 5
exclusiveZone: 0
implicitWidth: 400
implicitHeight: 50
color: "transparent"
// An empty click mask prevents the window from blocking mouse events.
mask: Region {}
Rectangle {
anchors.fill: parent
radius: height / 2
color: "#80000000"
RowLayout {
anchors {
fill: parent
leftMargin: 10
rightMargin: 15
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 24
color: "white"
text: root.volumeGlyph()
}
Rectangle {
// Stretches to fill all left-over space
Layout.fillWidth: true
implicitHeight: 10
radius: 20
color: "#50ffffff"
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
// Dim the fill while muted as an extra visual cue.
color: (Pipewire.defaultAudioSink?.audio.muted ?? false) ? "#80ffffff" : "white"
implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
radius: parent.radius
}
}
}
}
}
}
}
+211
View File
@@ -0,0 +1,211 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.UPower
Item {
id: root
readonly property var battery: UPower.displayDevice
readonly property bool charging: !UPower.onBattery
implicitHeight: label.implicitHeight
implicitWidth: label.implicitWidth
function fmtTime(seconds: real): string {
if (!seconds || seconds <= 0)
return "\u2014";
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
return h > 0 ? `${h}h ${m}m` : `${m}m`;
}
function batteryGlyph(): string {
if (!root.battery)
return "\u{F0091}";
const percent = Math.round(root.battery.percentage * 100);
if (root.charging)
return "\u{F0084}";
if (percent > 80)
return "\u{F0079}";
if (percent > 60)
return "\u{F0080}";
if (percent > 40)
return "\u{F007F}";
if (percent > 20)
return "\u{F007E}";
return "\u{F007A}";
}
function stateText(s: int): string {
switch (s) {
case UPowerDeviceState.Charging:
return "Charging";
case UPowerDeviceState.Discharging:
return "Discharging";
case UPowerDeviceState.Empty:
return "Empty";
case UPowerDeviceState.FullyCharged:
return "Fully charged";
case UPowerDeviceState.PendingCharge:
return "Pending charge";
case UPowerDeviceState.PendingDischarge:
return "Pending discharge";
default:
return "Unknown";
}
}
MouseArea {
id: mousearea
anchors.fill: parent
hoverEnabled: true
Text {
id: label
anchors.centerIn: parent
font.family: "JetBrainsMono Nerd Font"
color: "#cdd6f4"
text: {
if (!root.battery)
return "No Battery";
return root.batteryGlyph() + " " + Math.round(root.battery.percentage * 100) + "%";
}
}
}
PopupWindow {
id: popup
visible: mousearea.containsMouse && root.battery
anchor {
item: label
edges: Edges.Bottom
rect {
y: 30
}
}
color: "transparent"
implicitWidth: 270
implicitHeight: card.height
Rectangle {
id: card
width: 270
height: col.implicitHeight + 24
radius: 16
color: "#f21e1e2e"
border.color: "#45475a"
border.width: 1
ColumnLayout {
id: col
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: 12
}
spacing: 8
RowLayout {
Layout.fillWidth: true
spacing: 12
Text {
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 30
color: root.charging ? "#a6e3a1" : "#89b4fa"
text: root.batteryGlyph()
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Text {
color: "#cdd6f4"
font.pixelSize: 22
font.bold: true
text: root.battery ? Math.round(root.battery.percentage * 100) + "%" : "\u2014"
}
Text {
color: root.charging ? "#a6e3a1" : "#a6adc8"
font.pixelSize: 12
text: root.battery ? root.stateText(root.battery.state) : ""
}
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 1
color: "#313244"
}
InfoRow {
label: "Power draw"
value: root.battery ? Math.abs(root.battery.changeRate).toFixed(1) + " W" : "\u2014"
}
InfoRow {
visible: root.charging
label: "Time to full"
value: root.battery ? root.fmtTime(root.battery.timeToFull) : "\u2014"
}
InfoRow {
visible: !root.charging
label: "Time to empty"
value: root.battery ? root.fmtTime(root.battery.timeToEmpty) : "\u2014"
}
InfoRow {
label: "Energy"
value: root.battery ? root.battery.energy.toFixed(1) + " / " + root.battery.energyCapacity.toFixed(1) + " Wh" : "\u2014"
}
InfoRow {
visible: root.battery && root.battery.healthSupported
label: "Health"
value: root.battery ? Math.round(root.battery.healthPercentage) + "%" : "\u2014"
}
}
}
}
component InfoRow: RowLayout {
id: infoRow
property string label: ""
property string value: ""
Layout.fillWidth: true
spacing: 8
Text {
color: "#a6adc8"
font.pixelSize: 12
text: infoRow.label
}
Item {
Layout.fillWidth: true
}
Text {
color: "#cdd6f4"
font.pixelSize: 12
text: infoRow.value
}
}
}
@@ -0,0 +1,216 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import Quickshell.Hyprland
Item {
id: root
property var popups: null
readonly property bool expanded: popups ? popups.current === "bluetooth" : false
readonly property var adapter: Bluetooth.defaultAdapter
readonly property bool enabled: adapter?.enabled ?? false
// Reactive: reading each device's `connected` inside the binding makes it
// re-evaluate whenever any device connects/disconnects.
readonly property bool anyConnected: {
const ds = Bluetooth.devices?.values ?? [];
return ds.some(d => d.connected);
}
implicitWidth: icon.implicitWidth
implicitHeight: 30
Text {
id: icon
anchors.centerIn: parent
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 17
color: root.enabled ? (root.anyConnected ? "#a6e3a1" : "#89b4fa") : "#6c7086"
text: !root.enabled ? "\u{F00B2}" : (root.anyConnected ? "\u{F00B1}" : "\u{F00AF}")
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: if (root.popups) root.popups.toggle("bluetooth")
}
LazyLoader {
active: root.expanded
PanelWindow {
id: win
anchors.top: true
anchors.right: true
margins.top: 6
margins.right: 8
implicitWidth: 340
implicitHeight: card.implicitHeight
color: "transparent"
exclusiveZone: 0
HyprlandFocusGrab {
active: true
windows: [win]
onCleared: if (root.popups) root.popups.current = ""
}
Rectangle {
id: card
width: parent.width
implicitHeight: col.implicitHeight + 24
radius: 16
color: "#f21e1e2e"
border.color: "#45475a"
border.width: 1
ColumnLayout {
id: col
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 12
spacing: 10
RowLayout {
Layout.fillWidth: true
spacing: 8
Text {
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 16
color: "#cdd6f4"
text: "\u{F00AF} Bluetooth"
}
Item { Layout.fillWidth: true }
Pill {
visible: root.enabled
active: root.adapter?.discovering ?? false
text: (root.adapter?.discovering ?? false) ? "Scanning…" : "Scan"
onClicked: {
if (root.adapter)
root.adapter.discovering = !root.adapter.discovering;
}
}
Pill {
active: root.enabled
text: root.enabled ? "On" : "Off"
onClicked: {
if (root.adapter)
root.adapter.enabled = !root.enabled;
}
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 1
color: "#313244"
}
Text {
visible: !root.enabled
Layout.fillWidth: true
color: "#6c7086"
font.pixelSize: 13
text: "Bluetooth is off"
}
Text {
visible: root.enabled && (Bluetooth.devices?.values.length ?? 0) === 0
Layout.fillWidth: true
color: "#6c7086"
font.pixelSize: 13
text: "No devices found — tap Scan"
}
Repeater {
model: root.enabled ? (Bluetooth.devices?.values ?? []) : []
delegate: RowLayout {
id: drow
required property var modelData
readonly property bool busy: modelData.state === BluetoothDeviceState.Connecting
|| modelData.state === BluetoothDeviceState.Disconnecting
Layout.fillWidth: true
spacing: 8
Text {
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 16
color: drow.modelData.connected ? "#a6e3a1" : "#cdd6f4"
text: "\u{F00AF}"
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Text {
Layout.fillWidth: true
elide: Text.ElideRight
color: "#cdd6f4"
font.pixelSize: 13
text: drow.modelData.name || drow.modelData.address
}
Text {
visible: drow.modelData.connected
color: "#a6e3a1"
font.pixelSize: 11
text: drow.modelData.batteryAvailable
? "Connected · " + Math.round(drow.modelData.battery * 100) + "%"
: "Connected"
}
}
Pill {
active: drow.modelData.connected
text: drow.busy ? "…" : (drow.modelData.connected ? "Disconnect" : "Connect")
onClicked: drow.modelData.connected = !drow.modelData.connected
}
}
}
}
}
}
}
component Pill: Rectangle {
id: pill
property string text: ""
property bool active: false
signal clicked
implicitWidth: pillLabel.implicitWidth + 18
implicitHeight: pillLabel.implicitHeight + 8
radius: height / 2
color: pill.active ? "#89b4fa" : "#313244"
Text {
id: pillLabel
anchors.centerIn: parent
text: pill.text
color: pill.active ? "#11111b" : "#cdd6f4"
font.pixelSize: 12
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: pill.clicked()
}
}
}
+231
View File
@@ -0,0 +1,231 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
Item {
id: root
property var popups: null
readonly property bool expanded: popups ? popups.current === "clock" : false
property var now: new Date()
property int viewYear: now.getFullYear()
property int viewMonth: now.getMonth()
implicitWidth: label.implicitWidth
implicitHeight: 30
function shiftMonth(delta: int): void {
let m = root.viewMonth + delta;
let y = root.viewYear;
if (m < 0) {
m = 11;
y -= 1;
} else if (m > 11) {
m = 0;
y += 1;
}
root.viewMonth = m;
root.viewYear = y;
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: root.now = new Date()
}
// Snap the calendar back to the current month each time it's opened.
onExpandedChanged: {
if (expanded) {
root.viewYear = root.now.getFullYear();
root.viewMonth = root.now.getMonth();
}
}
Text {
id: label
anchors.centerIn: parent
color: "#cdd6f4"
text: Qt.formatDateTime(root.now, "hh:mm:ss dd MMM")
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: if (root.popups) root.popups.toggle("clock")
}
LazyLoader {
active: root.expanded
PanelWindow {
id: win
anchors.top: true
anchors.right: true
margins.top: 6
margins.right: 8
implicitWidth: 300
implicitHeight: card.implicitHeight
color: "transparent"
exclusiveZone: 0
HyprlandFocusGrab {
active: true
windows: [win]
onCleared: if (root.popups) root.popups.current = ""
}
Rectangle {
id: card
width: parent.width
implicitHeight: col.implicitHeight + 24
radius: 16
color: "#f21e1e2e"
border.color: "#45475a"
border.width: 1
ColumnLayout {
id: col
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 12
spacing: 10
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Text {
color: "#89b4fa"
font.pixelSize: 26
font.bold: true
text: Qt.formatDateTime(root.now, "dddd")
}
Text {
color: "#a6adc8"
font.pixelSize: 13
text: Qt.formatDateTime(root.now, "dd MMMM yyyy \u00B7 hh:mm:ss")
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 1
color: "#313244"
}
RowLayout {
Layout.fillWidth: true
spacing: 4
Text {
Layout.fillWidth: true
color: "#cdd6f4"
font.pixelSize: 14
font.bold: true
text: grid.title
}
NavBtn {
glyph: "\u{F0141}"
onClicked: root.shiftMonth(-1)
}
NavBtn {
glyph: "\u{F0142}"
onClicked: root.shiftMonth(1)
}
}
DayOfWeekRow {
Layout.fillWidth: true
locale: grid.locale
delegate: Text {
required property string shortName
horizontalAlignment: Text.AlignHCenter
color: "#6c7086"
font.pixelSize: 11
font.bold: true
text: shortName
}
}
MonthGrid {
id: grid
Layout.fillWidth: true
month: root.viewMonth
year: root.viewYear
spacing: 2
delegate: Item {
id: cell
required property var model
readonly property bool isToday: model.today
readonly property bool inMonth: model.month === grid.month
implicitWidth: 36
implicitHeight: 30
Rectangle {
anchors.centerIn: parent
width: 26
height: 26
radius: 13
color: cell.isToday ? "#89b4fa" : "transparent"
}
Text {
anchors.centerIn: parent
text: cell.model.day
font.pixelSize: 12
color: cell.isToday ? "#11111b" : (cell.inMonth ? "#cdd6f4" : "#585b70")
}
}
}
}
}
}
}
component NavBtn: Rectangle {
id: navBtn
property string glyph: ""
signal clicked
implicitWidth: 26
implicitHeight: 26
radius: 13
color: navMouse.containsMouse ? "#313244" : "transparent"
Text {
anchors.centerIn: parent
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 16
color: "#cdd6f4"
text: navBtn.glyph
}
MouseArea {
id: navMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: navBtn.clicked()
}
}
}
+224
View File
@@ -0,0 +1,224 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
RowLayout {
id: root
spacing: 4
readonly property int maxSlots: 20
readonly property var focusedWs: Hyprland.focusedWorkspace
// Show every regular workspace up to the highest occupied/focused one, plus
// one spare empty slot (the "+1"). e.g. 1 busy -> show 1,2 ; 3 busy -> 1..4.
readonly property int slotCount: {
let hi = 0;
const vals = Hyprland.workspaces.values;
for (let i = 0; i < vals.length; i++) {
const w = vals[i];
if (w.id > 0 && w.id > hi)
hi = w.id;
}
const f = Hyprland.focusedWorkspace;
if (f && f.id > hi)
hi = f.id;
return Math.min(root.maxSlots, hi + 1);
}
// Existing special workspaces (id < 0).
readonly property var specialList: {
const out = [];
const vals = Hyprland.workspaces.values;
for (let i = 0; i < vals.length; i++) {
if (vals[i].id < 0)
out.push(vals[i]);
}
return out;
}
function wsFor(id: int): var {
const vals = Hyprland.workspaces.values;
for (let i = 0; i < vals.length; i++) {
if (vals[i].id === id)
return vals[i];
}
return null;
}
// Hyprland runs in Lua dispatch mode here, so dispatchers take a Lua
// expression. Shell out to hyprctl with the raw Lua form.
Process {
id: dispatcher
}
function focusWorkspace(id: int): void {
dispatcher.exec(["hyprctl", "dispatch", "hl.dsp.focus({ workspace = " + id + " })"]);
}
function toggleSpecial(name: string): void {
dispatcher.exec(["hyprctl", "dispatch", "hl.dsp.workspace.toggle_special(\"" + name + "\")"]);
}
// Quickshell's workspace `active`/`focused` don't reflect whether a special
// is currently shown on a monitor, so we track it ourselves. Map of
// monitorName -> shown special id (0 = none). A special chip is "active"
// when its id appears here.
property var shownSpecials: ({})
function isSpecialShown(id: int): bool {
const m = root.shownSpecials;
for (const k in m) {
if (m[k] === id)
return true;
}
return false;
}
Connections {
target: Hyprland
function onRawEvent(event: var): void {
if (event.name !== "activespecialv2")
return;
// data: "<id>,<name>,<monitor>" (id/name empty when hidden)
const parts = event.data.split(",");
const mon = parts[parts.length - 1];
const id = parts[0] === "" ? 0 : parseInt(parts[0]);
const next = Object.assign({}, root.shownSpecials);
next[mon] = id;
root.shownSpecials = next;
}
}
// Seed the shown-special state at startup (no event has fired yet).
Process {
id: seedProc
running: true
command: ["hyprctl", "-j", "monitors"]
stdout: StdioCollector {
id: seedOut
onStreamFinished: {
try {
const mons = JSON.parse(seedOut.text);
const next = ({});
for (const m of mons) {
const sw = m.specialWorkspace;
next[m.name] = sw ? sw.id : 0;
}
root.shownSpecials = next;
} catch (e) {
// ignore malformed output
}
}
}
}
// ---- Regular workspaces ----
Repeater {
model: root.slotCount
delegate: Rectangle {
id: chip
required property int index
readonly property int wsId: index + 1
readonly property var ws: root.wsFor(wsId)
readonly property bool active: root.focusedWs ? root.focusedWs.id === wsId : false
readonly property bool urgent: ws ? ws.urgent : false
// A non-focused workspace only persists in Hyprland while occupied.
readonly property bool occupied: ws !== null && !active
implicitWidth: Math.max(22, txt.implicitWidth + 12)
implicitHeight: 22
radius: 11
color: chip.urgent ? "#f38ba8"
: chip.active ? "#89b4fa"
: (mouse.containsMouse ? "#313244" : "transparent")
Behavior on color {
ColorAnimation { duration: 120 }
}
Text {
id: txt
anchors.centerIn: parent
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 13
font.bold: chip.active
text: chip.wsId
color: (chip.active || chip.urgent) ? "#11111b"
: chip.occupied ? "#cdd6f4"
: "#585b70"
}
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.focusWorkspace(chip.wsId)
}
}
}
// ---- Separator (only when specials exist) ----
Rectangle {
visible: root.specialList.length > 0
implicitWidth: 1
implicitHeight: 14
color: "#45475a"
Layout.leftMargin: 2
Layout.rightMargin: 2
}
// ---- Special workspaces ----
Repeater {
model: root.specialList
delegate: Rectangle {
id: spc
required property var modelData
readonly property bool active: root.isSpecialShown(modelData.id)
readonly property bool urgent: modelData.urgent
readonly property string shortName: {
const n = modelData.name || "";
return n.startsWith("special:") ? n.slice(8) : n;
}
implicitWidth: Math.max(22, slabel.implicitWidth + 12)
implicitHeight: 22
radius: 11
color: spc.urgent ? "#f38ba8"
: spc.active ? "#cba6f7"
: (smouse.containsMouse ? "#313244" : "transparent")
Behavior on color {
ColorAnimation { duration: 120 }
}
Text {
id: slabel
anchors.centerIn: parent
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 13
font.bold: spc.active
text: "\u{F04CE}" + (spc.shortName ? " " + spc.shortName : "")
color: (spc.active || spc.urgent) ? "#11111b" : "#cba6f7"
}
MouseArea {
id: smouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleSpecial(spc.shortName)
}
}
}
}
@@ -0,0 +1,442 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
Item {
id: root
property var popups: null
readonly property bool expanded: popups ? popups.current === "network" : false
property bool wifiEnabled: true
property var networks: []
property string activeSsid: ""
property int activeSignal: 0
property var knownSsids: ({})
property string statusMsg: ""
property string pendingSsid: ""
implicitWidth: icon.implicitWidth
implicitHeight: 30
function secured(sec) {
return !!sec && sec.trim().length > 0;
}
function signalGlyph(s) {
if (s >= 80) return "\u{F0928}";
if (s >= 55) return "\u{F0925}";
if (s >= 30) return "\u{F0922}";
return "\u{F091F}";
}
function wifiGlyph() {
if (!wifiEnabled) return "\u{F05AA}";
if (activeSsid === "") return "\u{F05A9}";
return signalGlyph(activeSignal);
}
// nmcli -t escapes ':' as '\:' and '\' as '\\'; split on unescaped colons.
function splitFields(line) {
const out = [];
let cur = "";
for (let i = 0; i < line.length; i++) {
const c = line[i];
if (c === "\\" && i + 1 < line.length) {
cur += line[i + 1];
i++;
} else if (c === ":") {
out.push(cur);
cur = "";
} else {
cur += c;
}
}
out.push(cur);
return out;
}
function refresh() {
statusProc.running = true;
listProc.running = true;
knownProc.running = true;
}
function setWifi(on) {
radioProc.command = ["nmcli", "radio", "wifi", on ? "on" : "off"];
radioProc.running = true;
root.wifiEnabled = on;
Qt.callLater(root.refresh);
}
function rescan() {
root.statusMsg = "Scanning…";
rescanProc.running = true;
}
function doConnect(ssid, password) {
root.statusMsg = "Connecting to " + ssid + "…";
const cmd = ["nmcli", "device", "wifi", "connect", ssid];
if (password && password.length > 0)
cmd.push("password", password);
connectProc.command = cmd;
connectProc.running = true;
root.pendingSsid = "";
}
function disconnect(ssid) {
root.statusMsg = "Disconnecting…";
actionProc.command = ["nmcli", "connection", "down", "id", ssid];
actionProc.running = true;
}
function onNetworkClicked(net) {
if (net.active) {
root.disconnect(net.ssid);
return;
}
if (root.secured(net.security) && !root.knownSsids[net.ssid]) {
root.pendingSsid = (root.pendingSsid === net.ssid) ? "" : net.ssid;
return;
}
root.doConnect(net.ssid, "");
}
Process {
id: statusProc
command: ["nmcli", "-t", "-f", "WIFI", "radio"]
stdout: StdioCollector {
id: statusOut
onStreamFinished: root.wifiEnabled = statusOut.text.trim() === "enabled"
}
}
Process {
id: knownProc
command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"]
stdout: StdioCollector {
id: knownOut
onStreamFinished: {
const map = ({});
const lines = knownOut.text.split("\n");
for (const line of lines) {
if (!line) continue;
const f = root.splitFields(line);
if (f[1] && f[1].indexOf("wireless") >= 0)
map[f[0]] = true;
}
root.knownSsids = map;
}
}
}
Process {
id: listProc
command: ["nmcli", "-t", "-f", "ACTIVE,SSID,SIGNAL,SECURITY", "device", "wifi", "list"]
stdout: StdioCollector {
id: listOut
onStreamFinished: {
const seen = ({});
const list = [];
let actSsid = "";
let actSig = 0;
const lines = listOut.text.split("\n");
for (const line of lines) {
if (!line) continue;
const f = root.splitFields(line);
const active = f[0] === "yes";
const ssid = f[1] || "";
const sig = parseInt(f[2] || "0") || 0;
const sec = f[3] || "";
if (ssid === "") continue;
if (active) {
actSsid = ssid;
actSig = sig;
}
if (seen[ssid] !== undefined) {
const ex = list[seen[ssid]];
if (sig > ex.signal) ex.signal = sig;
if (active) ex.active = true;
continue;
}
seen[ssid] = list.length;
list.push({ ssid: ssid, signal: sig, security: sec, active: active });
}
list.sort((a, b) => (b.active - a.active) || (b.signal - a.signal));
root.networks = list;
root.activeSsid = actSsid;
root.activeSignal = actSig;
}
}
}
Process { id: radioProc }
Process {
id: rescanProc
command: ["nmcli", "device", "wifi", "rescan"]
onExited: {
root.statusMsg = "";
root.refresh();
}
}
Process {
id: connectProc
stdout: StdioCollector {
id: connectOut
onStreamFinished: {
root.statusMsg = "";
root.refresh();
}
}
stderr: StdioCollector {
id: connectErr
onStreamFinished: {
const t = connectErr.text.trim();
if (t !== "")
root.statusMsg = t.replace(/^Error:\s*/, "");
}
}
}
Process {
id: actionProc
onExited: root.refresh()
}
onExpandedChanged: if (expanded) refresh()
Timer {
interval: 8000
running: root.expanded
repeat: true
onTriggered: root.refresh()
}
Component.onCompleted: refresh()
Text {
id: icon
anchors.centerIn: parent
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 17
color: root.wifiEnabled ? (root.activeSsid !== "" ? "#89b4fa" : "#cdd6f4") : "#6c7086"
text: root.wifiGlyph()
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: if (root.popups) root.popups.toggle("network")
}
LazyLoader {
active: root.expanded
PanelWindow {
id: win
focusable: true
anchors.top: true
anchors.right: true
margins.top: 6
margins.right: 8
implicitWidth: 360
implicitHeight: card.implicitHeight
color: "transparent"
exclusiveZone: 0
HyprlandFocusGrab {
active: true
windows: [win]
onCleared: if (root.popups) root.popups.current = ""
}
Rectangle {
id: card
width: parent.width
implicitHeight: col.implicitHeight + 24
radius: 16
color: "#f21e1e2e"
border.color: "#45475a"
border.width: 1
ColumnLayout {
id: col
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 12
spacing: 8
RowLayout {
Layout.fillWidth: true
spacing: 8
Text {
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 16
color: "#cdd6f4"
text: "\u{F05A9} Wi-Fi"
}
Item { Layout.fillWidth: true }
Pill {
visible: root.wifiEnabled
text: "Rescan"
onClicked: root.rescan()
}
Pill {
active: root.wifiEnabled
text: root.wifiEnabled ? "On" : "Off"
onClicked: root.setWifi(!root.wifiEnabled)
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 1
color: "#313244"
}
Text {
visible: !root.wifiEnabled
color: "#6c7086"
font.pixelSize: 13
text: "Wi-Fi is off"
}
Text {
visible: root.statusMsg !== ""
Layout.fillWidth: true
wrapMode: Text.WordWrap
color: "#f9e2af"
font.pixelSize: 11
text: root.statusMsg
}
Repeater {
model: root.wifiEnabled ? root.networks : []
delegate: ColumnLayout {
id: nrow
required property var modelData
Layout.fillWidth: true
spacing: 4
RowLayout {
Layout.fillWidth: true
spacing: 8
Text {
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 15
color: nrow.modelData.active ? "#a6e3a1" : "#cdd6f4"
text: root.signalGlyph(nrow.modelData.signal)
}
Text {
Layout.fillWidth: true
elide: Text.ElideRight
color: nrow.modelData.active ? "#a6e3a1" : "#cdd6f4"
font.pixelSize: 13
text: nrow.modelData.ssid
}
Text {
visible: root.secured(nrow.modelData.security)
font.family: "JetBrainsMono Nerd Font"
font.pixelSize: 12
color: "#6c7086"
text: "\u{F033E}"
}
Pill {
active: nrow.modelData.active
text: nrow.modelData.active ? "Disconnect" : "Connect"
onClicked: root.onNetworkClicked(nrow.modelData)
}
}
RowLayout {
id: pwRow
visible: root.pendingSsid === nrow.modelData.ssid
Layout.fillWidth: true
spacing: 6
onVisibleChanged: if (visible) pwField.forceActiveFocus()
Rectangle {
Layout.fillWidth: true
implicitHeight: 30
radius: 8
color: "#181825"
border.color: pwField.activeFocus ? "#89b4fa" : "#45475a"
border.width: 1
TextField {
id: pwField
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
verticalAlignment: TextInput.AlignVCenter
echoMode: TextInput.Password
placeholderText: "Password"
color: "#cdd6f4"
placeholderTextColor: "#6c7086"
font.pixelSize: 12
background: null
onAccepted: root.doConnect(nrow.modelData.ssid, text)
}
}
Pill {
active: true
text: "Join"
onClicked: root.doConnect(nrow.modelData.ssid, pwField.text)
}
}
}
}
}
}
}
}
component Pill: Rectangle {
id: pill
property string text: ""
property bool active: false
signal clicked
implicitWidth: pillLabel.implicitWidth + 18
implicitHeight: pillLabel.implicitHeight + 8
radius: height / 2
color: pill.active ? "#89b4fa" : "#313244"
Text {
id: pillLabel
anchors.centerIn: parent
text: pill.text
color: pill.active ? "#11111b" : "#cdd6f4"
font.pixelSize: 12
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: pill.clicked()
}
}
}
+23
View File
@@ -0,0 +1,23 @@
import QtQuick
import Quickshell
Text {
required property var onClicked
function click() {
if (mouseArea.containsMouse) {
onClicked();
}
}
color: "#fff"
text: "Sound"
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
}
}
+20
View File
@@ -0,0 +1,20 @@
import QtQuick
import Quickshell
import "./modules"
ShellRoot {
id: root
function toggleAudioMixer() {
audioMixer.visible = !audioMixer.visible;
}
AudioMixer {
id: audioMixer
visible: false
}
OSD {}
BrightnessOSD {}
Bar {}
}