From 9e14c809a815fea442ae258baf353f02547b420c Mon Sep 17 00:00:00 2001 From: Kavin-Charles Date: Wed, 29 Jul 2026 14:18:19 +0530 Subject: [PATCH 1/6] feat(waybar): anchor wofi menus to cursor position Read cursor x from hyprctl and clamp against the focused monitor width so menus open next to the clicked module instead of screen centre. Falls back to 1920 when the monitor query fails. --- bin/waybar-battery-menu.sh | 7 +++++-- bin/waybar-bluetooth-menu.sh | 5 ++++- bin/waybar-clock-menu.sh | 5 ++++- bin/waybar-network-menu.sh | 6 ++++-- bin/waybar-pulseaudio-menu.sh | 5 ++++- bin/waybar-spotify-menu.sh | 5 ++++- bin/waybar-vpn-menu.sh | 7 +++++-- 7 files changed, 30 insertions(+), 10 deletions(-) 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-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" From 048578a8a81df83f671ca89047b272bd402c02c5 Mon Sep 17 00:00:00 2001 From: Kavin-Charles Date: Wed, 29 Jul 2026 14:18:19 +0530 Subject: [PATCH 2/6] fix(waybar): repair vpn indicator conditional The [[ test was closed with a single bracket, so the script aborted on every poll. Output is now icon-only; the connection name moved to the drawer. --- bin/waybar-vpn.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 62017b959a5cd2ae05731c5298cd05c3522c746c Mon Sep 17 00:00:00 2001 From: Kavin-Charles Date: Wed, 29 Jul 2026 14:18:19 +0530 Subject: [PATCH 3/6] feat(waybar): route module clicks to ags drawers Replace the shared waybar-tray-menu.sh handler with per-module ags-toggle.sh drawers and drop the now-redundant custom/settings module and its styles. --- .config/waybar/config.jsonc | 25 +++++++++---------------- .config/waybar/style.css | 9 --------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc index c65a64f..c2fd138 100644 --- a/.config/waybar/config.jsonc +++ b/.config/waybar/config.jsonc @@ -4,7 +4,7 @@ "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-right": ["network", "bluetooth", "custom/vpn", "pulseaudio", "battery", "clock"], "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,15 +93,7 @@ "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": { @@ -118,6 +111,6 @@ "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" } } 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; } From 3a8876f84baf4cfc58ef1b2b7240ac7ee45990ba Mon Sep 17 00:00:00 2001 From: Kavin-Charles Date: Wed, 29 Jul 2026 14:18:19 +0530 Subject: [PATCH 4/6] feat(hypr): autostart ags on hyprland start --- .config/hypr/hyprland.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/hypr/hyprland.lua b/.config/hypr/hyprland.lua index 6d87b7b..4caf9ad 100644 --- a/.config/hypr/hyprland.lua +++ b/.config/hypr/hyprland.lua @@ -56,6 +56,7 @@ 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") From 06f9644c3440e55018c29b276359f1413b2e4df6 Mon Sep 17 00:00:00 2001 From: Kavin-Charles Date: Wed, 29 Jul 2026 14:18:19 +0530 Subject: [PATCH 5/6] chore: stop ignoring README.md --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index ff8fe35..61ef661 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ *.bak* -README.md From f0d261c8ddf335dfb73553e141832c7487a1aa4c Mon Sep 17 00:00:00 2001 From: Kavin-Charles Date: Mon, 3 Aug 2026 12:38:24 +0530 Subject: [PATCH 6/6] chore(dotfiles): sync Monday August 3 --- .config/ags/.gitignore | 2 + .config/ags/app.ts | 22 +++ .config/ags/drawers/AudioDrawer.tsx | 38 ++++ .config/ags/drawers/BatteryDrawer.tsx | 39 ++++ .config/ags/drawers/BluetoothDrawer.tsx | 38 ++++ .config/ags/drawers/CalendarDrawer.tsx | 43 +++++ .config/ags/drawers/NetworkDrawer.tsx | 38 ++++ .config/ags/env.d.ts | 21 +++ .config/ags/package.json | 10 ++ .config/ags/style.scss | 64 +++++++ .config/ags/tsconfig.json | 14 ++ .config/ags/widget/Bar.tsx | 39 ++++ .config/btop/themes/velocity.theme | 47 +++++ .config/hypr/hyprland.conf | 85 --------- .config/hypr/hyprland.lua | 10 +- .config/mimeapps.list | 16 ++ .config/waybar/config.jsonc | 12 +- .gitconfig | 3 + bin/ags-toggle.sh | 23 +++ bin/waybar-tray-menu.sh | 226 +----------------------- 20 files changed, 475 insertions(+), 315 deletions(-) create mode 100644 .config/ags/.gitignore create mode 100644 .config/ags/app.ts create mode 100644 .config/ags/drawers/AudioDrawer.tsx create mode 100644 .config/ags/drawers/BatteryDrawer.tsx create mode 100644 .config/ags/drawers/BluetoothDrawer.tsx create mode 100644 .config/ags/drawers/CalendarDrawer.tsx create mode 100644 .config/ags/drawers/NetworkDrawer.tsx create mode 100644 .config/ags/env.d.ts create mode 100644 .config/ags/package.json create mode 100644 .config/ags/style.scss create mode 100644 .config/ags/tsconfig.json create mode 100644 .config/ags/widget/Bar.tsx create mode 100644 .config/btop/themes/velocity.theme delete mode 100644 .config/hypr/hyprland.conf create mode 100755 bin/ags-toggle.sh 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 4caf9ad..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, }) @@ -60,6 +60,7 @@ hl.on("hyprland.start", function () 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) @@ -256,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 c2fd138..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", "clock"], + "modules-center": ["custom/spotify", "clock"], + "modules-right": ["network", "bluetooth", "custom/vpn", "pulseaudio", "battery", "tray"], "hyprland/workspaces": { "disable-scroll": true, @@ -98,7 +98,7 @@ "clock": { "interval": 1, - "format": "󰥔 {:%H:%M}", + "format": "󰥔 {:%I:%M %p}", "format-alt": " {:%a, %d %b %Y}", "tooltip-format": "{calendar}", "calendar": { @@ -112,5 +112,11 @@ "on-scroll-down": "shift_down" }, "on-click": "$HOME/bin/ags-toggle.sh calendar-drawer" + }, + + "tray": { + "spacing": 8, + "icon-size": 16, + "icon-theme": "Adwaita" } } 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/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-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