diff --git a/.config/ags/.gitignore b/.config/ags/.gitignore
new file mode 100644
index 0000000..298eb4d
--- /dev/null
+++ b/.config/ags/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+@girs/
diff --git a/.config/ags/app.ts b/.config/ags/app.ts
new file mode 100644
index 0000000..dbfd5b3
--- /dev/null
+++ b/.config/ags/app.ts
@@ -0,0 +1,22 @@
+import app from "ags/gtk4/app"
+import style from "./style.scss"
+import { execAsync } from "ags/process"
+import NetworkDrawer from "./drawers/NetworkDrawer"
+import BluetoothDrawer from "./drawers/BluetoothDrawer"
+import AudioDrawer from "./drawers/AudioDrawer"
+import BatteryDrawer from "./drawers/BatteryDrawer"
+import CalendarDrawer from "./drawers/CalendarDrawer"
+
+app.start({
+ css: style,
+ main() {
+ execAsync("rm -f /tmp/ags-drawer-open")
+ app.get_monitors().map((m) => {
+ NetworkDrawer(m)
+ BluetoothDrawer(m)
+ AudioDrawer(m)
+ BatteryDrawer(m)
+ CalendarDrawer(m)
+ })
+ },
+})
diff --git a/.config/ags/drawers/AudioDrawer.tsx b/.config/ags/drawers/AudioDrawer.tsx
new file mode 100644
index 0000000..80c802e
--- /dev/null
+++ b/.config/ags/drawers/AudioDrawer.tsx
@@ -0,0 +1,38 @@
+import app from "ags/gtk4/app"
+import { Astal, Gtk } from "ags/gtk4"
+import { execAsync } from "ags/process"
+import GLib from "gi://GLib"
+
+const { TOP, RIGHT } = Astal.WindowAnchor
+
+export default function AudioDrawer(gdkmonitor: any) {
+ return (
+ {
+ self.connect("notify::is-active", () => {
+ if (!self.isActive && self.visible) {
+ if (GLib.file_test("/tmp/ags-drawer-lock", GLib.FileTest.EXISTS)) return
+ self.visible = false
+ GLib.file_set_contents("/tmp/ags-drawer-open", "")
+ }
+ })
+ }}
+ >
+
+
+
+
+
+ )
+}
diff --git a/.config/ags/drawers/BatteryDrawer.tsx b/.config/ags/drawers/BatteryDrawer.tsx
new file mode 100644
index 0000000..4bc9a97
--- /dev/null
+++ b/.config/ags/drawers/BatteryDrawer.tsx
@@ -0,0 +1,39 @@
+import app from "ags/gtk4/app"
+import { Astal, Gtk } from "ags/gtk4"
+import { createPoll } from "ags/time"
+import GLib from "gi://GLib"
+
+const { TOP, RIGHT } = Astal.WindowAnchor
+
+export default function BatteryDrawer(gdkmonitor: any) {
+ const info = createPoll(" N/A", 10000,
+ `sh -c 'upower -i $(upower -e | grep BAT 2>/dev/null) 2>/dev/null | awk -F":\\\\t*" "/percentage/{p=\\$2} /state/{s=\\$2} /time to (empty|full)/{t=\\$2} END{printf \" %s | %s | %s\", p?p:\"?\", s?s:\"?\", t?t:\"N/A\"}"'`)
+
+ return (
+ {
+ self.connect("notify::is-active", () => {
+ if (!self.isActive && self.visible) {
+ if (GLib.file_test("/tmp/ags-drawer-lock", GLib.FileTest.EXISTS)) return
+ self.visible = false
+ GLib.file_set_contents("/tmp/ags-drawer-open", "")
+ }
+ })
+ }}
+ >
+
+
+
+
+
+ )
+}
diff --git a/.config/ags/drawers/BluetoothDrawer.tsx b/.config/ags/drawers/BluetoothDrawer.tsx
new file mode 100644
index 0000000..e8cdb23
--- /dev/null
+++ b/.config/ags/drawers/BluetoothDrawer.tsx
@@ -0,0 +1,38 @@
+import app from "ags/gtk4/app"
+import { Astal, Gtk } from "ags/gtk4"
+import { execAsync } from "ags/process"
+import GLib from "gi://GLib"
+
+const { TOP, RIGHT } = Astal.WindowAnchor
+
+export default function BluetoothDrawer(gdkmonitor: any) {
+ return (
+ {
+ self.connect("notify::is-active", () => {
+ if (!self.isActive && self.visible) {
+ if (GLib.file_test("/tmp/ags-drawer-lock", GLib.FileTest.EXISTS)) return
+ self.visible = false
+ GLib.file_set_contents("/tmp/ags-drawer-open", "")
+ }
+ })
+ }}
+ >
+
+
+
+
+
+ )
+}
diff --git a/.config/ags/drawers/CalendarDrawer.tsx b/.config/ags/drawers/CalendarDrawer.tsx
new file mode 100644
index 0000000..f28fdc2
--- /dev/null
+++ b/.config/ags/drawers/CalendarDrawer.tsx
@@ -0,0 +1,43 @@
+import app from "ags/gtk4/app"
+import { Astal, Gtk } from "ags/gtk4"
+import { createPoll } from "ags/time"
+import GLib from "gi://GLib"
+
+const { TOP, RIGHT } = Astal.WindowAnchor
+
+export default function CalendarDrawer(gdkmonitor: any) {
+ const time = createPoll("", 1000, "date '+ %H:%M:%S'")
+ const date = createPoll("", 60000, "date '+ %A, %d %B %Y'")
+
+ return (
+ {
+ self.connect("notify::is-active", () => {
+ if (!self.isActive && self.visible) {
+ if (GLib.file_test("/tmp/ags-drawer-lock", GLib.FileTest.EXISTS)) return
+ self.visible = false
+ GLib.file_set_contents("/tmp/ags-drawer-open", "")
+ }
+ })
+ }}
+ >
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/.config/ags/drawers/NetworkDrawer.tsx b/.config/ags/drawers/NetworkDrawer.tsx
new file mode 100644
index 0000000..bafcefe
--- /dev/null
+++ b/.config/ags/drawers/NetworkDrawer.tsx
@@ -0,0 +1,38 @@
+import app from "ags/gtk4/app"
+import { Astal, Gtk } from "ags/gtk4"
+import { execAsync } from "ags/process"
+import GLib from "gi://GLib"
+
+const { TOP, RIGHT } = Astal.WindowAnchor
+
+export default function NetworkDrawer(gdkmonitor: any) {
+ return (
+ {
+ self.connect("notify::is-active", () => {
+ if (!self.isActive && self.visible) {
+ if (GLib.file_test("/tmp/ags-drawer-lock", GLib.FileTest.EXISTS)) return
+ self.visible = false
+ GLib.file_set_contents("/tmp/ags-drawer-open", "")
+ }
+ })
+ }}
+ >
+
+
+
+
+
+ )
+}
diff --git a/.config/ags/env.d.ts b/.config/ags/env.d.ts
new file mode 100644
index 0000000..792ebfd
--- /dev/null
+++ b/.config/ags/env.d.ts
@@ -0,0 +1,21 @@
+declare const SRC: string
+
+declare module "inline:*" {
+ const content: string
+ export default content
+}
+
+declare module "*.scss" {
+ const content: string
+ export default content
+}
+
+declare module "*.blp" {
+ const content: string
+ export default content
+}
+
+declare module "*.css" {
+ const content: string
+ export default content
+}
diff --git a/.config/ags/package.json b/.config/ags/package.json
new file mode 100644
index 0000000..cbf736c
--- /dev/null
+++ b/.config/ags/package.json
@@ -0,0 +1,10 @@
+{
+ "dependencies": {
+ "ags": "*",
+ "gnim": "*"
+ },
+ "prettier": {
+ "semi": false,
+ "tabWidth": 2
+ }
+}
diff --git a/.config/ags/style.scss b/.config/ags/style.scss
new file mode 100644
index 0000000..ba2ba3a
--- /dev/null
+++ b/.config/ags/style.scss
@@ -0,0 +1,64 @@
+* {
+ all: unset;
+ font-family: "Ubuntu Nerd Font";
+ font-size: 13px;
+}
+
+window.Drawer {
+ background: transparent;
+}
+
+window.Drawer > box {
+ margin: 8px;
+ padding: 12px;
+ min-width: 280px;
+ border: 1px solid #45475a;
+ border-radius: 12px;
+ background: #1c1c1c;
+ color: #cdd6f4;
+}
+
+.drawer-title {
+ font-size: 14px;
+ font-weight: bold;
+ color: #89b4fa;
+ margin-bottom: 8px;
+}
+
+.drawer-item {
+ padding: 8px 12px;
+ border-radius: 8px;
+}
+
+.drawer-item:hover {
+ background: #313244;
+}
+
+.drawer-label {
+ color: #cdd6f4;
+}
+
+.drawer-icon {
+ margin-right: 8px;
+}
+
+button {
+ padding: 6px 12px;
+ border-radius: 8px;
+ background: #313244;
+ color: #cdd6f4;
+}
+
+button:hover {
+ background: #45475a;
+}
+
+button.danger:hover {
+ background: #f38ba8;
+ color: #1c1c1c;
+}
+
+button.primary:hover {
+ background: #89b4fa;
+ color: #1c1c1c;
+}
diff --git a/.config/ags/tsconfig.json b/.config/ags/tsconfig.json
new file mode 100644
index 0000000..2bb1f01
--- /dev/null
+++ b/.config/ags/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "strict": true,
+ "module": "ES2022",
+ "target": "ES2020",
+ "lib": ["ES2023"],
+ "moduleResolution": "Bundler",
+ // "checkJs": true,
+ // "allowJs": true,
+ "jsx": "react-jsx",
+ "jsxImportSource": "ags/gtk4"
+ }
+}
diff --git a/.config/ags/widget/Bar.tsx b/.config/ags/widget/Bar.tsx
new file mode 100644
index 0000000..56aa86a
--- /dev/null
+++ b/.config/ags/widget/Bar.tsx
@@ -0,0 +1,39 @@
+import app from "ags/gtk4/app"
+import { Astal, Gtk, Gdk } from "ags/gtk4"
+import { execAsync } from "ags/process"
+import { createPoll } from "ags/time"
+
+export default function Bar(gdkmonitor: Gdk.Monitor) {
+ const time = createPoll("", 1000, "date")
+ const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/.config/btop/themes/velocity.theme b/.config/btop/themes/velocity.theme
new file mode 100644
index 0000000..eaecec1
--- /dev/null
+++ b/.config/btop/themes/velocity.theme
@@ -0,0 +1,47 @@
+# Velocity Theme by perfektnacht; btop.theme
+
+theme[main_bg]="#0A0A0A"
+theme[main_fg]="#F8F8F2"
+theme[title]="#00FFF0"
+theme[hi_fg]="#4A9EF1"
+theme[selected_bg]="#4A9EF1"
+theme[selected_fg]="#0A0A0A"
+theme[inactive_fg]="#888888"
+theme[cpu_box]="#4A9EF1"
+theme[mem_box]="#4ADF86"
+theme[net_box]="#FF8C42"
+theme[proc_box]="#FF4757"
+theme[div_line]="#2D2D30"
+
+# Gradient for all meters and graphs
+theme[temp_start]="#4ADF86" # Success green (cool)
+theme[temp_mid]="#FF8C42" # Warning orange (warm)
+theme[temp_end]="#FF4757" # Velocity red (hot)
+
+theme[cpu_start]="#4ADF86" # Success green (low usage)
+theme[cpu_mid]="#FF8C42" # Warning orange (medium)
+theme[cpu_end]="#FF4757" # Velocity red (high usage)
+
+theme[free_start]="#4ADF86" # Success green (lots of free memory)
+theme[free_mid]="#FF8C42" # Warning orange (some free)
+theme[free_end]="#FF4757" # Velocity red (low free memory)
+
+theme[cached_start]="#4A9EF1" # Steel blue (low cache)
+theme[cached_mid]="#00FFF0" # Neon cyan (medium cache)
+theme[cached_end]="#4ADF86" # Success green (high cache - good!)
+
+theme[available_start]="#4ADF86" # Success green (high available)
+theme[available_mid]="#FF8C42" # Warning orange (medium)
+theme[available_end]="#FF4757" # Velocity red (low available)
+
+theme[used_start]="#4ADF86" # Success green (low usage)
+theme[used_mid]="#FF8C42" # Warning orange (medium)
+theme[used_end]="#FF4757" # Velocity red (high usage)
+
+theme[download_start]="#4A9EF1" # Steel blue (low download)
+theme[download_mid]="#00FFF0" # Neon cyan (medium)
+theme[download_end]="#4ADF86" # Success green (high download - good!)
+
+theme[upload_start]="#FF8C42" # Warning orange (low upload)
+theme[upload_mid]="#FF4757" # Velocity red (medium)
+theme[upload_end]="#FFD700" # Gold (high upload - like victory!)
diff --git a/.config/hypr/hyprland.conf b/.config/hypr/hyprland.conf
deleted file mode 100644
index d5fcd1a..0000000
--- a/.config/hypr/hyprland.conf
+++ /dev/null
@@ -1,85 +0,0 @@
-monitor=eDP-1,1920x1080@60,0x0,1
-
-exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
-
-input {
- kb_layout = us
- follow_mouse = 1
- touchpad {
- natural_scroll = yes
- }
- sensitivity = 0
-}
-
-general {
- gaps_in = 5
- gaps_out = 10
- border_size = 2
- col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
- col.inactive_border = rgba(595959aa)
- layout = dwindle
-}
-
-decoration {
- rounding = 10
- blur {
- enabled = true
- size = 3
- passes = 1
- }
- shadow {
- enabled = true
- range = 4
- render_power = 3
- color = rgba(1a1a1aee)
- }
-}
-
-animations {
- enabled = yes
- bezier = myBezier, 0.05, 0.9, 0.1, 1.05
- animation = windows, 1, 7, myBezier
- animation = windowsOut, 1, 7, default
- animation = border, 1, 10, default
- animation = borderangle, 1, 8, default
- animation = fade, 1, 7, default
- animation = workspaces, 1, 6, default
-}
-
-dwindle {
- pseudotile = yes
- master_switch = 0
- preserve_split = yes
-}
-
-master {
- new_is_master = true
-}
-
-gestures {
- workspace_swipe = off
-}
-
-windowrulev2 = nomaximizerequest, class:.* # You'll probably like this
-
-bind = SUPER, Q, exec, alacritty
-bind = SUPER, T, exec, thunar
-bind = SUPER, W, killactive,
-bind = SUPER, E, exit,
-bind = SUPER, F, fullscreen
-bind = SUPER, L, exec, hyprlock
-bind = SUPER, V, togglefloating,
-bind = SUPER, R, exec, rofi -show drun
-bind = SUPER, P, pseudo,
-bind = SUPER, J, togglesplit,
-bind = SUPER, left, movefocus, l
-bind = SUPER, right, movefocus, r
-bind = SUPER, up, movefocus, u
-bind = SUPER, down, movefocus, d
-bind = SUPER, 1, workspace, 1
-bind = SUPER, 2, workspace, 2
-bind = SUPER, 3, workspace, 3
-bind = SUPER, 4, workspace, 4
-bind = SUPER, 5, workspace, 5
-bind = SUPER, mouse_down, workspace, e+1
-bind = SUPER, mouse_up, workspace, e-1
diff --git a/.config/hypr/hyprland.lua b/.config/hypr/hyprland.lua
index 6d87b7b..0d7e5b8 100644
--- a/.config/hypr/hyprland.lua
+++ b/.config/hypr/hyprland.lua
@@ -24,10 +24,10 @@ hl.config({ autogenerated = false }) -- remove this line to remove the warning
-- See https://wiki.hypr.land/Configuring/Basics/Monitors/
hl.monitor({
- output = "",
- mode = "preferred",
- position = "auto",
- scale = "auto",
+ output = "eDP-1",
+ mode = "1920x1080@60.01Hz",
+ position = "0x0",
+ scale = 1,
})
@@ -56,9 +56,11 @@ hl.on("hyprland.start", function ()
hl.exec_cmd("swaybg -i /home/kavin/Downloads/wallpaper -m fill")
hl.exec_cmd("walker --gapplication-service")
hl.exec_cmd("waybar")
+ hl.exec_cmd("ags run -d /home/kavin/.config/ags")
hl.exec_cmd("nm-applet")
hl.exec_cmd("snappy-switcher --daemon -c /home/kavin/.config/snappy-switcher/config.ini")
hl.exec_cmd("/usr/lib/gnome-keyring-daemon --start --components=secrets,ssh,pkcs11")
+ hl.exec_cmd("qs -c tray")
end)
@@ -255,7 +257,6 @@ hl.device({
local mainMod = "SUPER" -- Sets "Windows" key as main modifier
-- Core binds
-hl.bind("CTRL + B", hl.dsp.exec_cmd(browser)) -- default browser (ctrl+b)
hl.bind(mainMod .. " + Return", hl.dsp.exec_cmd(terminal)) -- terminal
hl.bind(mainMod .. " + Q", hl.dsp.window.close()) -- close window
hl.bind(mainMod .. " + W", hl.dsp.window.close()) -- close window (alt)
diff --git a/.config/mimeapps.list b/.config/mimeapps.list
index 67c2e14..2f28a58 100644
--- a/.config/mimeapps.list
+++ b/.config/mimeapps.list
@@ -2,5 +2,21 @@
x-scheme-handler/discord=vesktop.desktop
x-scheme-handler/claude-cli=claude-code-url-handler.desktop
x-scheme-handler/claude=com.anthropic.Claude.desktop
+x-scheme-handler/antigravity=antigravity.desktop
+x-scheme-handler/http=firefox.desktop
+x-scheme-handler/https=firefox.desktop
+x-scheme-handler/chrome=firefox.desktop
+text/html=firefox.desktop
+application/x-extension-htm=firefox.desktop
+application/x-extension-html=firefox.desktop
+application/x-extension-shtml=firefox.desktop
+application/xhtml+xml=firefox.desktop
+application/x-extension-xhtml=firefox.desktop
+application/x-extension-xht=firefox.desktop
+application/zip=org.kde.dolphin.desktop
[Added Associations]
+x-scheme-handler/http=firefox.desktop;
+x-scheme-handler/https=firefox.desktop;
+x-scheme-handler/chrome=firefox.desktop;
+application/zip=org.kde.dolphin.desktop;
diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc
index c65a64f..170bc73 100644
--- a/.config/waybar/config.jsonc
+++ b/.config/waybar/config.jsonc
@@ -3,8 +3,8 @@
"position": "top",
"reload_style_on_change": true,
"modules-left": ["hyprland/workspaces"],
- "modules-center": ["custom/spotify"],
- "modules-right": ["network", "bluetooth", "custom/vpn", "pulseaudio", "battery", "custom/settings", "clock"],
+ "modules-center": ["custom/spotify", "clock"],
+ "modules-right": ["network", "bluetooth", "custom/vpn", "pulseaudio", "battery", "tray"],
"hyprland/workspaces": {
"disable-scroll": true,
@@ -50,7 +50,7 @@
"tooltip-format-ethernet": "{ifname} {ipaddr}",
"tooltip-format-disconnected": "Disconnected",
"format-icons": ["", "", "", "", ""],
- "on-click": "$HOME/bin/waybar-tray-menu.sh"
+ "on-click": "$HOME/bin/ags-toggle.sh network-drawer"
},
"bluetooth": {
@@ -61,19 +61,20 @@
"tooltip-format": "Bluetooth on",
"tooltip-format-connected": "{device_enumerate}",
"tooltip-format-enumerate-connected": "{device_alias}",
- "on-click": "$HOME/bin/waybar-tray-menu.sh"
+ "on-click": "$HOME/bin/ags-toggle.sh bluetooth-drawer"
},
"custom/vpn": {
- "format": "",
+ "format": "{}",
"interval": 1,
"exec": "$HOME/bin/waybar-vpn.sh",
- "on-click": "$HOME/bin/waybar-tray-menu.sh"
+ "on-click": "$HOME/bin/ags-toggle.sh network-drawer",
+ "tooltip": false
},
"pulseaudio": {
"scroll-step": 2,
- "on-click": "$HOME/bin/waybar-tray-menu.sh",
+ "on-click": "$HOME/bin/ags-toggle.sh audio-drawer",
"format": "{icon}",
"format-muted": "",
"tooltip-format": "{desc} {volume}%",
@@ -92,20 +93,12 @@
"format-plugged": "",
"tooltip-format": "{timeTo}",
"format-icons": ["", "", "", "", "", "", "", "", "", ""],
- "on-click": "$HOME/bin/waybar-tray-menu.sh"
- },
-
- "custom/settings": {
- "format": "{}",
- "interval": 3600,
- "exec": "echo ' '",
- "on-click": "$HOME/bin/waybar-tray-menu.sh",
- "tooltip": false
+ "on-click": "$HOME/bin/ags-toggle.sh battery-drawer"
},
"clock": {
"interval": 1,
- "format": " {:%H:%M}",
+ "format": " {:%I:%M %p}",
"format-alt": " {:%a, %d %b %Y}",
"tooltip-format": "{calendar}",
"calendar": {
@@ -118,6 +111,12 @@
"on-scroll-up": "shift_up",
"on-scroll-down": "shift_down"
},
- "on-click": "$HOME/bin/waybar-tray-menu.sh"
+ "on-click": "$HOME/bin/ags-toggle.sh calendar-drawer"
+ },
+
+ "tray": {
+ "spacing": 8,
+ "icon-size": 16,
+ "icon-theme": "Adwaita"
}
}
diff --git a/.config/waybar/style.css b/.config/waybar/style.css
index 32d8d23..133f10d 100644
--- a/.config/waybar/style.css
+++ b/.config/waybar/style.css
@@ -17,7 +17,6 @@ window#waybar {
#network,
#pulseaudio,
#battery,
-#custom-settings,
#clock {
padding: 0 6px;
}
@@ -98,14 +97,6 @@ window#waybar {
color: #94e2d5;
}
-#custom-settings {
- color: #cdd6f4;
-}
-
-#custom-settings:hover {
- color: #b4befe;
-}
-
#clock {
color: #74c7ec;
}
diff --git a/.gitconfig b/.gitconfig
index b4f468b..3313c65 100644
--- a/.gitconfig
+++ b/.gitconfig
@@ -4,3 +4,6 @@
[credential "https://gist.github.com"]
helper =
helper = !/usr/bin/gh auth git-credential
+[user]
+ email = kavincharles@gmail.com
+ name = Kavin-Charles
diff --git a/.gitignore b/.gitignore
index ff8fe35..61ef661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
*.bak*
-README.md
diff --git a/bin/ags-toggle.sh b/bin/ags-toggle.sh
new file mode 100755
index 0000000..d3e4922
--- /dev/null
+++ b/bin/ags-toggle.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+target="$1"
+state_file="/tmp/ags-drawer-open"
+lock_file="/tmp/ags-drawer-lock"
+
+# Lock to prevent auto-close from racing with this toggle
+touch "$lock_file"
+
+current=$(cat "$state_file" 2>/dev/null || echo "")
+
+if [ "$current" = "$target" ]; then
+ rm -f "$state_file"
+else
+ [ -n "$current" ] && ags toggle "$current" >/dev/null 2>&1 || true
+ echo "$target" > "$state_file"
+fi
+
+ags toggle "$target" >/dev/null 2>&1 || true
+
+# Keep lock briefly to ensure auto-close sees it
+(sleep 0.3; rm -f "$lock_file") &
diff --git a/bin/waybar-battery-menu.sh b/bin/waybar-battery-menu.sh
index 223e765..c29ba57 100755
--- a/bin/waybar-battery-menu.sh
+++ b/bin/waybar-battery-menu.sh
@@ -2,11 +2,14 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 400 --lines 8 --cache-file /dev/null
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 400 ? w - 410 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 400 --lines 8 --cache-file /dev/null --location 0 --xoffset "$x" --yoffset 30
}
info=$(upower -i $(upower -e | grep BAT) 2>/dev/null)
-[ -z "$info" ] && { notify-send "Battery" "No battery found"; exit 1; }
+[ -z "$info" ] && { command -v notify-send >/dev/null && notify-send "Battery" "No battery found"; exit 1; }
percentage=$(echo "$info" | awk -F':[ \t]*' '/percentage/ {print $2}')
state=$(echo "$info" | awk -F':[ \t]*' '/state/ {print $2}')
diff --git a/bin/waybar-bluetooth-menu.sh b/bin/waybar-bluetooth-menu.sh
index 0725579..e90ce1f 100755
--- a/bin/waybar-bluetooth-menu.sh
+++ b/bin/waybar-bluetooth-menu.sh
@@ -2,7 +2,10 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --matching fuzzy --prompt "$1" --width 520 --lines 6 --cache-file /dev/null
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 520 ? w - 530 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --matching fuzzy --prompt "$1" --width 520 --lines 6 --cache-file /dev/null --location 0 --xoffset "$x" --yoffset 30
}
notify() {
diff --git a/bin/waybar-clock-menu.sh b/bin/waybar-clock-menu.sh
index 1b5156f..28a0279 100755
--- a/bin/waybar-clock-menu.sh
+++ b/bin/waybar-clock-menu.sh
@@ -2,7 +2,10 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 400 --lines 12 --cache-file /dev/null
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 400 ? w - 410 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 400 --lines 12 --cache-file /dev/null --location 0 --xoffset "$x" --yoffset 30
}
output="$(date '+ %H:%M:%S')\n$(date '+ %A, %d %B %Y')\n\n$(cal -3)"
diff --git a/bin/waybar-network-menu.sh b/bin/waybar-network-menu.sh
index 4e265b8..6621700 100755
--- a/bin/waybar-network-menu.sh
+++ b/bin/waybar-network-menu.sh
@@ -2,7 +2,10 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 800 --lines 6 --cache-file /dev/null
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 800 ? w - 810 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 800 --lines 6 --cache-file /dev/null --location 0 --xoffset "$x" --yoffset 30
}
notify() {
@@ -28,7 +31,6 @@ toggle=" Turn Wi-Fi Off"
[ "$wifi_state" = "disabled" ] && toggle=" Turn Wi-Fi On"
rescan=" Rescan Networks"
-notify "Scanning for Wi-Fi networks..."
mapfile -t nets < <(nmcli -t -f IN-USE,SSID,SIGNAL device wifi list ifname "$iface")
options="$toggle\n$rescan"
diff --git a/bin/waybar-pulseaudio-menu.sh b/bin/waybar-pulseaudio-menu.sh
index 16af10e..00e0d1a 100755
--- a/bin/waybar-pulseaudio-menu.sh
+++ b/bin/waybar-pulseaudio-menu.sh
@@ -2,7 +2,10 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 520
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 520 ? w - 530 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 520 --location 0 --xoffset "$x" --yoffset 30
}
notify() {
diff --git a/bin/waybar-spotify-menu.sh b/bin/waybar-spotify-menu.sh
index 129ee9d..d58130c 100755
--- a/bin/waybar-spotify-menu.sh
+++ b/bin/waybar-spotify-menu.sh
@@ -2,7 +2,10 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 400 --cache-file /dev/null
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 400 ? w - 410 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 400 --cache-file /dev/null --location 0 --xoffset "$x" --yoffset 30
}
notify() {
diff --git a/bin/waybar-tray-menu.sh b/bin/waybar-tray-menu.sh
index 989b503..25fe1b0 100755
--- a/bin/waybar-tray-menu.sh
+++ b/bin/waybar-tray-menu.sh
@@ -1,227 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
-declare -A SINK_MAP
-declare -A NET_MAP
-declare -A BT_MAP
-declare -A BT_CONN
-declare -A VPN_MAP
-
-notify() {
- command -v notify-send >/dev/null && notify-send "$@"
+qs -c tray ipc call tray ping &>/dev/null || {
+ qs -c tray &>/dev/null &
+ sleep 2
}
-entries=""
-
-# Audio
-entries+="─── Audio ───\n"
-entries+=" Toggle Mute\n"
-default_sink=$(pactl get-default-sink 2>/dev/null || true)
-mapfile -t sinks < <(pactl list short sinks 2>/dev/null || true)
-for s in "${sinks[@]}"; do
- IFS=$'\t' read -r id name driver _ <<< "$s"
- vol=$(pactl get-sink-volume "$name" 2>/dev/null | awk -F'/' 'NR==1{gsub(/ /,"",$2);print $2}' || echo "?")
- mute=$(pactl get-sink-mute "$name" 2>/dev/null | awk '{print $2}' || echo "no")
- icon=""
- case "$name" in *bluez*) icon="" ;; *hdmi*) icon="" ;; *headphone*|*headset*) icon="" ;; *speaker*) icon="" ;; esac
- [ "$mute" = "yes" ] && icon=""
- prefix=" "
- [ "$name" = "$default_sink" ] && prefix=" "
- label=$(sed -e 's/alsa_output\.//g' -e 's/bluez_output\.//g' -e 's/\.analog-stereo//g' -e 's/_/ /g' <<< "$name")
- entry="$prefix$icon $label ${vol:-?%}"
- entries+="$entry\n"
- SINK_MAP["$entry"]="$name"
-done
-entries+=" Open Pavucontrol\n"
-
-# Wi-Fi
-entries+="─── Network ───\n"
-iface=$(nmcli -t -f DEVICE,TYPE device status 2>/dev/null | awk -F: '$2=="wifi"{print $1;exit}' || true)
-wifi_state=$(nmcli radio wifi 2>/dev/null || echo "unknown")
-if [ "$wifi_state" = "disabled" ]; then
- entries+=" Turn Wi-Fi On\n"
-else
- entries+=" Turn Wi-Fi Off\n"
- entries+=" Rescan Networks\n"
-fi
-if [ "$wifi_state" = "enabled" ] && [ -n "$iface" ]; then
- mapfile -t nets < <(nmcli -t -f IN-USE,SSID,SIGNAL,SECURITY device wifi list ifname "$iface" 2>/dev/null | sort -t: -k3 -rn | head -10)
- for n in "${nets[@]}"; do
- IFS=: read -r inuse ssid signal sec <<< "$n"
- [ -z "$ssid" ] && continue
- if ((signal>=80)); then icon2=""
- elif ((signal>=60)); then icon2=""
- elif ((signal>=40)); then icon2=""
- elif ((signal>=20)); then icon2=""
- else icon2=""
- fi
- prefix2=" "
- [ "$inuse" = "*" ] && prefix2=" "
- entry2="$prefix2$icon2 $signal% $ssid"
- entries+="$entry2\n"
- NET_MAP["$entry2"]="$ssid"
- done
-fi
-entries+=" Open Network Manager\n"
-
-# Bluetooth
-entries+="─── Bluetooth ───\n"
-powered=$(bluetoothctl show 2>/dev/null | awk -F': ' '/Powered:/ {print $2}' || echo "no")
-if [ "$powered" = "no" ]; then
- entries+=" Turn Bluetooth On\n"
-else
- entries+=" Turn Bluetooth Off\n"
-fi
-mapfile -t bt_devices < <({
- bluetoothctl devices Connected 2>/dev/null
- bluetoothctl devices Paired 2>/dev/null
-} | awk '!seen[$2]++')
-for d in "${bt_devices[@]}"; do
- mac=${d#Device }
- mac=${mac%% *}
- name=${d#Device ??\:??\:??\:??\:??\:?? }
- [ -z "$name" ] && name="$mac"
- connected="no"
- bluetoothctl devices Connected 2>/dev/null | grep -q "$mac" && connected="yes"
- icon3=""
- prefix3=" "
- [ "$connected" = "yes" ] && icon3="" && prefix3=" "
- entry3="$prefix3$icon3 $name"
- [ "$connected" = "yes" ] && batt=$(bluetoothctl info "$mac" 2>/dev/null | sed -n 's/Battery.*(\([0-9]\+\))$/\1/p' | head -n1) && [ -n "$batt" ] && entry3+=" ${batt}%"
- entries+="$entry3\n"
- BT_MAP["$entry3"]="$mac"
- BT_CONN["$entry3"]="$connected"
-done
-entries+=" Open Bluetooth Manager\n"
-
-# VPN
-entries+="─── VPN ───\n"
-while IFS= read -r conn; do
- name2="${conn%%:*}"
- [ -z "$name2" ] && continue
- active=$(nmcli -t -f NAME,ACTIVE connection show "$name2" 2>/dev/null | awk -F: 'NR==1{print $2}')
- prefix4=" "
- [ "$active" = "yes" ] && prefix4=" "
- entries+="$prefix4 $name2\n"
- VPN_MAP["$prefix4 $name2"]="$name2"
-done < <(nmcli -t -f NAME,TYPE connection show 2>/dev/null | grep ':vpn\|:wireguard' | sed 's/:vpn$//;s/:wireguard$//')
-
-# Battery
-entries+="─── Battery ───\n"
-bat_info=$(upower -i "$(upower -e 2>/dev/null | grep BAT)" 2>/dev/null || true)
-if [ -n "$bat_info" ]; then
- percentage=$(echo "$bat_info" | awk -F':[ \t]*' '/percentage/ {print $2}')
- state=$(echo "$bat_info" | awk -F':[ \t]*' '/state/ {print $2}')
- time_bat=$(echo "$bat_info" | awk -F':[ \t]*' '/time to empty/ {print $2}')
- [ -z "$time_bat" ] && time_bat=$(echo "$bat_info" | awk -F':[ \t]*' '/time to full/ {print $2}')
- [ -z "$time_bat" ] && time_bat="N/A"
- entries+=" $percentage\n"
- entries+=" $time_bat\n"
- entries+=" $state\n"
-else
- entries+=" No battery\n"
-fi
-
-# Clock
-entries+="─── Clock ───\n"
-entries+=" $(date '+%H:%M:%S')\n"
-entries+=" $(date '+%A, %d %B %Y')\n"
-entries+="$(cal 2>/dev/null | sed 's/^/ /')\n"
-
-printf '%b\n' "$entries" > /tmp/waybar-menu.txt
-
-choice=$(wofi --dmenu --prompt "System Tray" --width 500 --height 400 --conf /dev/null --style /dev/null < /tmp/waybar-menu.txt) || true
-[ -z "$choice" ] && exit 0
-
-# Audio actions
-if [ "$choice" = " Toggle Mute" ]; then
- pactl set-sink-mute @DEFAULT_SINK@ toggle
- state=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}')
- [ "$state" = "yes" ] && notify "Audio" "Muted" || notify "Audio" "Unmuted"
- exit 0
-fi
-
-if [ "$choice" = " Open Pavucontrol" ]; then
- pavucontrol >/dev/null 2>&1 &
- exit 0
-fi
-
-if [ "$choice" = " Turn Wi-Fi On" ]; then
- nmcli radio wifi on && notify "Network" "Wi-Fi enabled"
- exit 0
-fi
-
-if [ "$choice" = " Turn Wi-Fi Off" ]; then
- nmcli radio wifi off && notify "Network" "Wi-Fi disabled"
- exit 0
-fi
-
-if [ "$choice" = " Rescan Networks" ]; then
- nmcli device wifi rescan >/dev/null 2>&1 && notify "Network" "Scanning..." || true
- exit 0
-fi
-
-if [ "$choice" = " Open Network Manager" ]; then
- nm-connection-editor &
- exit 0
-fi
-
-net_ssid="${NET_MAP[$choice]:-}"
-if [ -n "$net_ssid" ]; then
- if nmcli device wifi connect "$net_ssid" >/dev/null 2>&1; then
- notify "Network" "Connected to $net_ssid"
- else
- pass=$(wofi --dmenu --prompt "Password for $net_ssid" --width 520 --conf /dev/null --style /dev/null 2>/dev/null)
- [ -n "$pass" ] && nmcli device wifi connect "$net_ssid" password "$pass" >/dev/null 2>&1 && notify "Network" "Connected to $net_ssid" || notify "Network" "Connection failed"
- fi
- exit 0
-fi
-
-if [ "$choice" = " Turn Bluetooth On" ]; then
- bluetoothctl power on >/dev/null && notify "Bluetooth" "Enabled"
- exit 0
-fi
-
-if [ "$choice" = " Turn Bluetooth Off" ]; then
- bluetoothctl power off >/dev/null && notify "Bluetooth" "Disabled"
- exit 0
-fi
-
-if [ "$choice" = " Open Bluetooth Manager" ]; then
- blueman-manager &
- exit 0
-fi
-
-bt_mac="${BT_MAP[$choice]:-}"
-if [ -n "$bt_mac" ]; then
- bt_state="${BT_CONN[$choice]:-}"
- powered=$(bluetoothctl show 2>/dev/null | awk -F': ' '/Powered:/ {print $2}' || echo "no")
- [ "$powered" = "no" ] && bluetoothctl power on >/dev/null
- if [ "$bt_state" = "yes" ]; then
- bluetoothctl disconnect "$bt_mac" >/dev/null && notify "Bluetooth" "Disconnected"
- else
- bluetoothctl connect "$bt_mac" >/dev/null && notify "Bluetooth" "Connected" || notify "Bluetooth" "Connection failed"
- fi
- exit 0
-fi
-
-vpn_name="${VPN_MAP[$choice]:-}"
-if [ -n "$vpn_name" ]; then
- active=$(nmcli -t -f NAME,ACTIVE connection show "$vpn_name" 2>/dev/null | awk -F: 'NR==1{print $2}')
- if [ "$active" = "yes" ]; then
- nmcli connection down "$vpn_name" >/dev/null && notify "VPN" "$vpn_name disconnected"
- else
- nmcli connection up "$vpn_name" >/dev/null && notify "VPN" "$vpn_name connected" || notify "VPN" "Connection failed"
- fi
- exit 0
-fi
-
-sink="${SINK_MAP[$choice]:-}"
-if [ -n "$sink" ]; then
- pactl set-default-sink "$sink"
- while read -r input _; do
- pactl move-sink-input "$input" "$sink"
- done < <(pactl list short sink-inputs 2>/dev/null)
- notify "Audio" "Switched output"
- exit 0
-fi
+qs -c tray ipc call tray toggle
diff --git a/bin/waybar-vpn-menu.sh b/bin/waybar-vpn-menu.sh
index 09aab52..1f9e1a9 100755
--- a/bin/waybar-vpn-menu.sh
+++ b/bin/waybar-vpn-menu.sh
@@ -2,7 +2,10 @@
set -euo pipefail
menu() {
- wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 520 --cache-file /dev/null
+ read -r x y < <(hyprctl cursorpos | tr , ' ')
+ read -r w < <(hyprctl monitors -j | jq -r '.[] | select(.focused).width' 2>/dev/null || echo 1920)
+ x=$(( x + 10 > w - 520 ? w - 530 : x < 10 ? 10 : x + 10 ))
+ wofi --dmenu --insensitive --prompt "$1" --matching fuzzy --width 520 --cache-file /dev/null --location 0 --xoffset "$x" --yoffset 30
}
notify() {
@@ -21,7 +24,7 @@ for conn in "${connections[@]}"; do
icon=""
prefix=" "
if [ "$active" = "yes" ]; then
- icon=""
+ icon=""
prefix=" "
fi
entry="$prefix$icon $name"
diff --git a/bin/waybar-vpn.sh b/bin/waybar-vpn.sh
index 303a632..a1a05c3 100755
--- a/bin/waybar-vpn.sh
+++ b/bin/waybar-vpn.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
vpn=$(nmcli -t -o connection show --active 2>/dev/null | grep -i vpn | head -1 | cut -d: -f1)
if [[ -n "$vpn" ]]; then
- echo " $vpn"
+ echo ""
else
echo ""
fi