diff --git a/config/ags/bar/dump b/config/ags/bar/dump
new file mode 100644
index 0000000..0d85b51
--- /dev/null
+++ b/config/ags/bar/dump
@@ -0,0 +1,886 @@
+
+import GLib from "gi://GLib";
+import Astal from "gi://Astal";
+import AstalHyprland from "gi://AstalHyprland";
+import Gio from "gi://Gio";
+import Gtk from "gi://Gtk";
+import Gdk from "gi://Gdk";
+import { exec } from "gi://GLib";
+
+function getSystemStats() {
+ const cpu = exec("sh -c 'top -bn1 | grep \"Cpu(s)\" | sed \"s/.*, *\\([0-9.]*\\)%* id.*/\\1/\" | awk \"{print 100 - $1}\"'");
+ const memory = exec("free | grep Mem | awk '{print $3/$2 * 100.0}'");
+ const brightness = exec("brightnessctl g");
+ const battery = exec("cat /sys/class/power_supply/BAT0/capacity");
+ return { cpu, memory, brightness, battery };
+}
+
+function getWorkspaceNumber() {
+ return AstalHyprland.get_default().get_active_workspace().get_id();
+}
+
+function getWindowTitle() {
+ return exec("xdotool getactivewindow getwindowname");
+}
+
+function getDateTime() {
+ return GLib.DateTime.new_now_local().format("%a %b %d, %Y %H:%M:%S");
+}
+
+function createQuickActionMenu() {
+ const menu = new Gtk.Menu();
+ const volumeItem = new Gtk.MenuItem({ label: "🔊 Volume" });
+ const micItem = new Gtk.MenuItem({ label: "🎙️ Microphone" });
+ const wifiItem = new Gtk.MenuItem({ label: "📶 WiFi" });
+ const powerItem = new Gtk.MenuItem({ label: "🔌 Power" });
+
+ menu.append(volumeItem);
+ menu.append(micItem);
+ menu.append(wifiItem);
+ menu.append(powerItem);
+
+ volumeItem.show();
+ micItem.show();
+ wifiItem.show();
+ powerItem.show();
+
+ return menu;
+}
+
+function Bar(gdkmonitor) {
+ const { cpu, memory, brightness, battery } = getSystemStats();
+ const workspaceNumber = getWorkspaceNumber();
+ const windowTitle = getWindowTitle();
+ const dateTime = getDateTime();
+ const quickActionMenu = createQuickActionMenu();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function main() {
+ for (const m of AstalHyprland.get_default().get_monitors()) {
+ Bar(m.gdk_monitor);
+ }
+}
+
+App.start({ main });
+
+
+
+
+import GLib from "gi://GLib";
+import Astal from "gi://Astal";
+import AstalHyprland from "gi://AstalHyprland";
+import Gtk from "gi://Gtk";
+import { exec } from "gi://GLib";
+import { createQuickActionsMenu } from "./quickactions.js";
+
+function getSystemStats() {
+ const cpu = exec("sh -c 'top -bn1 | grep \"Cpu(s)\" | sed \"s/.*, *\\([0-9.]*\\)%* id.*/\\1/\" | awk \"{print 100 - $1}\"'");
+ const memory = exec("free | grep Mem | awk '{print $3/$2 * 100.0}'");
+ const brightness = exec("brightnessctl g");
+ const battery = exec("cat /sys/class/power_supply/BAT0/capacity");
+ return { cpu, memory, brightness, battery };
+}
+
+function getWorkspaceNumber() {
+ return AstalHyprland.get_default().get_active_workspace().get_id();
+}
+
+function getWindowTitle() {
+ return exec("xdotool getactivewindow getwindowname");
+}
+
+function getDateTime() {
+ return GLib.DateTime.new_now_local().format("%a %b %d, %Y %H:%M:%S");
+}
+
+function Bar(gdkmonitor) {
+ const { cpu, memory, brightness, battery } = getSystemStats();
+ const workspaceNumber = getWorkspaceNumber();
+ const windowTitle = getWindowTitle();
+ const dateTime = getDateTime();
+ const quickActionMenu = createQuickActionsMenu();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function main() {
+ for (const m of AstalHyprland.get_default().get_monitors()) {
+ Bar(m.gdk_monitor);
+ }
+}
+
+App.start({ main });
+
+
+
+
+import AstalNetwork from "gi://AstalNetwork";
+import AstalBluetooth from "gi://AstalBluetooth";
+import Gtk from "gi://Gtk";
+
+function createQuickActionsMenu() {
+ const menu = new Gtk.Menu();
+
+ const wifiItem = new Gtk.MenuItem({ label: "📶 WiFi" });
+ const bluetoothItem = new Gtk.MenuItem({ label: "🔵 Bluetooth" });
+ const volumeItem = new Gtk.MenuItem({ label: "🔊 Volume" });
+ const micItem = new Gtk.MenuItem({ label: "🎙️ Microphone" });
+ const powerItem = new Gtk.MenuItem({ label: "🔌 Power" });
+
+ menu.append(wifiItem);
+ menu.append(bluetoothItem);
+ menu.append(volumeItem);
+ menu.append(micItem);
+ menu.append(powerItem);
+
+ wifiItem.connect("activate", () => toggleWiFi());
+ bluetoothItem.connect("activate", () => toggleBluetooth());
+ volumeItem.connect("activate", () => adjustVolume());
+ micItem.connect("activate", () => adjustMic());
+ powerItem.connect("activate", () => showPowerMenu());
+
+ wifiItem.show();
+ bluetoothItem.show();
+ volumeItem.show();
+ micItem.show();
+ powerItem.show();
+
+ return menu;
+}
+
+function toggleWiFi() {
+ const network = AstalNetwork.get_default();
+ const wifi = network.get_wifi();
+ const state = wifi.get_state();
+
+ if (state === AstalNetwork.WifiState.DISCONNECTED) {
+ wifi.connect();
+ } else {
+ wifi.disconnect();
+ }
+}
+
+function toggleBluetooth() {
+ const bluetooth = AstalBluetooth.get_default();
+ const adapter = bluetooth.get_adapter();
+ const powered = adapter.get_powered();
+
+ adapter.set_powered(!powered);
+}
+
+function adjustVolume() {
+ exec("pavucontrol");
+}
+
+function adjustMic() {
+ exec("pavucontrol");
+}
+
+function showPowerMenu() {
+ exec("power-menu");
+}
+
+export { createQuickActionsMenu };
+
+
+
+
+// colors.scss
+$accent: #4a90e2; // Primary accent color (change this)
+$bg: #1e1e2e; // Background
+$fg: #f8f8f2; // Foreground/text
+$hover: lighten($accent, 10%);
+$border: rgba(255, 255, 255, 0.1);
+
+
+
+@import "colors";
+
+* {
+ all: unset;
+ font-family: "JetBrainsMono Nerd Font", monospace;
+ font-size: 14px;
+ color: $fg;
+}
+
+window {
+ background-color: transparent;
+}
+
+box {
+ background-color: $bg;
+ border-radius: 16px;
+ padding: 6px 12px;
+ border: 1px solid $border;
+ margin: 8px;
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3);
+}
+
+label {
+ padding: 0 8px;
+}
+
+// Clickable buttons/icons (e.g. QuickAction, tray items)
+button {
+ background-color: transparent;
+ border-radius: 12px;
+ padding: 6px;
+ transition: background-color 0.2s ease-in-out;
+
+ &:hover {
+ background-color: $hover;
+ }
+}
+
+// System Tray area
+tray {
+ padding: 0 8px;
+}
+
+// Layout specifics
+.left-section,
+.center-section,
+.right-section {
+ padding: 0 12px;
+}
+
+.left-section {
+ border-right: 1px solid $border;
+}
+
+.right-section {
+ border-left: 1px solid $border;
+}
+
+// Icons (you'll replace them with fluentui icons)
+.icon {
+ font-family: "FluentUI System Icons"; // Ensure this font is loaded
+ font-size: 16px;
+}
+
+
+
+
+import Gtk from "gi://Gtk";
+import AstalNetwork from "gi://AstalNetwork";
+import AstalBluetooth from "gi://AstalBluetooth";
+import { exec } from "gi://GLib";
+
+let quickActionsMenu;
+
+// Toggle WiFi connection
+function toggleWiFi() {
+ const network = AstalNetwork.get_default();
+ const wifi = network.get_wifi();
+ const state = wifi.get_state();
+
+ if (state === AstalNetwork.WifiState.DISCONNECTED) {
+ wifi.connect();
+ } else {
+ wifi.disconnect();
+ }
+}
+
+// Toggle Bluetooth power state
+function toggleBluetooth() {
+ const bluetooth = AstalBluetooth.get_default();
+ const adapter = bluetooth.get_adapter();
+ const powered = adapter.get_powered();
+
+ adapter.set_powered(!powered);
+}
+
+// Adjust volume or microphone (opens pavucontrol)
+function adjustVolume() {
+ exec("pavucontrol");
+}
+
+function adjustMic() {
+ exec("pavucontrol");
+}
+
+// Power menu
+function showPowerMenu() {
+ exec("power-menu");
+}
+
+// Show WiFi network picker
+function pickWiFi() {
+ const wifi = AstalNetwork.get_default().get_wifi();
+ wifi.show_picker();
+}
+
+// Show Bluetooth device picker
+function pickBluetooth() {
+ const bluetooth = AstalBluetooth.get_default();
+ bluetooth.show_picker();
+}
+
+// Create menu using JSX
+function createQuickActionsMenu() {
+ quickActionsMenu = (
+
+ );
+
+ return quickActionsMenu;
+}
+
+// Export the function
+export { createQuickActionsMenu };
+
+
+
+
+
+import Gtk from "gi://Gtk";
+import AstalNetwork from "gi://AstalNetwork";
+import AstalBluetooth from "gi://AstalBluetooth";
+import { exec } from "gi://GLib";
+
+function toggleWiFi() {
+ const network = AstalNetwork.get_default();
+ const wifi = network.get_wifi();
+ wifi.get_state() === AstalNetwork.WifiState.DISCONNECTED
+ ? wifi.connect()
+ : wifi.disconnect();
+}
+
+function toggleBluetooth() {
+ const adapter = AstalBluetooth.get_default().get_adapter();
+ adapter.set_powered(!adapter.get_powered());
+}
+
+function adjustVolume() {
+ exec("pavucontrol");
+}
+
+function adjustMic() {
+ exec("pavucontrol");
+}
+
+function showPowerMenu() {
+ exec("power-menu");
+}
+
+// Build submenu for WiFi networks
+function buildWifiMenu() {
+ const wifi = AstalNetwork.get_default().get_wifi();
+ const menu = new Gtk.Menu();
+
+ for (const ap of wifi.get_access_points()) {
+ const ssid = ap.get_ssid();
+ const strength = ap.get_strength();
+ const item = new Gtk.MenuItem({ label: `${ssid} (${strength}%)` });
+
+ item.connect("activate", () => {
+ wifi.connect_access_point(ap);
+ });
+
+ item.show();
+ menu.append(item);
+ }
+
+ return menu;
+}
+
+// Build submenu for Bluetooth devices
+function buildBluetoothMenu() {
+ const bluetooth = AstalBluetooth.get_default();
+ const menu = new Gtk.Menu();
+
+ for (const device of bluetooth.get_devices()) {
+ const name = device.get_name();
+ const connected = device.get_connected();
+ const label = connected ? `✔ ${name}` : name;
+
+ const item = new Gtk.MenuItem({ label });
+
+ item.connect("activate", () => {
+ if (!connected) {
+ device.connect_device();
+ } else {
+ device.disconnect_device();
+ }
+ });
+
+ item.show();
+ menu.append(item);
+ }
+
+ return menu;
+}
+
+function createQuickActionsMenu() {
+ const wifiPicker = new Gtk.MenuItem({ label: "→ Pick Network ▸" });
+ const btPicker = new Gtk.MenuItem({ label: "→ Pick Device ▸" });
+
+ const wifiSubMenu = new Gtk.Menu();
+ const btSubMenu = new Gtk.Menu();
+
+ // Populate submenus on open
+ wifiPicker.connect("activate", () => {
+ wifiSubMenu.foreach(child => wifiSubMenu.remove(child));
+ const newMenu = buildWifiMenu();
+ newMenu.foreach(child => wifiSubMenu.append(child));
+ wifiSubMenu.show_all();
+ });
+
+ btPicker.connect("activate", () => {
+ btSubMenu.foreach(child => btSubMenu.remove(child));
+ const newMenu = buildBluetoothMenu();
+ newMenu.foreach(child => btSubMenu.append(child));
+ btSubMenu.show_all();
+ });
+
+ wifiPicker.set_submenu(wifiSubMenu);
+ btPicker.set_submenu(btSubMenu);
+
+ return (
+
+ );
+}
+
+export { createQuickActionsMenu };
+
+
+
+
+
+
+import Gtk from "gi://Gtk";
+import AstalNetwork from "gi://AstalNetwork";
+import AstalBluetooth from "gi://AstalBluetooth";
+import { exec } from "gi://GLib";
+
+function createToggleRow({ label, active, onToggle, onExpand }) {
+ return (
+
+
+
+
+ );
+}
+
+function createQuickActionsMenu() {
+ const wifi = AstalNetwork.get_default().get_wifi();
+ const bt = AstalBluetooth.get_default().get_adapter();
+
+ const wifiSub = new Gtk.Menu();
+ const btSub = new Gtk.Menu();
+
+ function refreshWifiMenu() {
+ wifiSub.foreach(c => wifiSub.remove(c));
+ for (const ap of wifi.get_access_points()) {
+ const item = new Gtk.MenuItem({ label: `${ap.get_ssid()} (${ap.get_strength()}%)` });
+ item.connect("activate", () => wifi.connect_access_point(ap));
+ item.show();
+ wifiSub.append(item);
+ }
+ }
+
+ function refreshBtMenu() {
+ btSub.foreach(c => btSub.remove(c));
+ for (const dev of AstalBluetooth.get_default().get_devices()) {
+ const name = dev.get_name();
+ const label = dev.get_connected() ? `✔ ${name}` : name;
+ const item = new Gtk.MenuItem({ label });
+ item.connect("activate", () => {
+ dev.get_connected() ? dev.disconnect_device() : dev.connect_device();
+ });
+ item.show();
+ btSub.append(item);
+ }
+ }
+
+ return (
+
+ );
+}
+
+export { createQuickActionsMenu };
+
+
+
+
+
+@import "colors";
+
+.toggle-row {
+ background-color: $bg;
+ border-radius: 12px;
+ margin: 6px 0;
+ overflow: hidden;
+ border: 1px solid $border;
+
+ button {
+ padding: 10px 16px;
+ font-size: 14px;
+ transition: background 0.2s ease;
+ border: none;
+ background: transparent;
+
+ &:hover {
+ background-color: $hover;
+ }
+ }
+
+ .toggle {
+ flex: 1;
+ background-color: transparent;
+ text-align: left;
+ &.active {
+ background-color: $accent;
+ color: white;
+ }
+ }
+
+ .arrow {
+ width: 40px;
+ background-color: transparent;
+ text-align: center;
+ }
+}
+
+
+.audio-control {
+ display: flex;
+ align-items: center;
+ padding: 8px 12px;
+ gap: 8px;
+
+ label {
+ min-width: 60px;
+ color: $fg;
+ }
+
+ scale {
+ flex: 1;
+ }
+
+ button {
+ background-color: transparent;
+ padding: 6px;
+ border-radius: 6px;
+
+ &:hover {
+ background-color: $hover;
+ }
+ }
+}
+
+.media-control {
+ padding: 12px;
+ text-align: center;
+
+ label {
+ display: block;
+ margin-bottom: 6px;
+ }
+
+ button {
+ margin: 0 4px;
+ padding: 6px 10px;
+ border-radius: 6px;
+
+ &:hover {
+ background-color: $hover;
+ }
+ }
+}
+
+
+
+
+
+
+import Gtk from "gi://Gtk";
+import AstalNetwork from "gi://AstalNetwork";
+import AstalBluetooth from "gi://AstalBluetooth";
+import AstalAudio from "gi://AstalAudio";
+import AstalMedia from "gi://AstalMedia";
+import { exec } from "gi://GLib";
+
+// Helpers
+function createToggleRow({ label, active, onToggle, onExpand }) {
+ return (
+
+
+
+
+ );
+}
+
+function createPowerRow({ onExpand }) {
+ return (
+
+
+
+
+ );
+}
+
+function createQuickActionsMenu() {
+ const wifi = AstalNetwork.get_default().get_wifi();
+ const bt = AstalBluetooth.get_default().get_adapter();
+ const audio = AstalAudio.get_default();
+ const media = AstalMedia.get_default();
+
+ // Submenus
+ const wifiSub = new Gtk.Menu();
+ const btSub = new Gtk.Menu();
+ const powerSub = new Gtk.Menu();
+ const outputSub = new Gtk.Menu();
+ const inputSub = new Gtk.Menu();
+
+ function refreshWifiMenu() {
+ wifiSub.foreach(c => wifiSub.remove(c));
+ for (const ap of wifi.get_access_points()) {
+ const item = new Gtk.MenuItem({ label: `${ap.get_ssid()} (${ap.get_strength()}%)` });
+ item.connect("activate", () => wifi.connect_access_point(ap));
+ item.show();
+ wifiSub.append(item);
+ }
+ }
+
+ function refreshBtMenu() {
+ btSub.foreach(c => btSub.remove(c));
+ for (const dev of AstalBluetooth.get_default().get_devices()) {
+ const label = dev.get_connected() ? `✔ ${dev.get_name()}` : dev.get_name();
+ const item = new Gtk.MenuItem({ label });
+ item.connect("activate", () =>
+ dev.get_connected() ? dev.disconnect_device() : dev.connect_device()
+ );
+ item.show();
+ btSub.append(item);
+ }
+ }
+
+ function refreshPowerMenu() {
+ const entries = [
+ { label: "⏻ Shutdown", cmd: "systemctl poweroff" },
+ { label: "🔁 Reboot", cmd: "systemctl reboot" },
+ { label: "🔒 Lock", cmd: "loginctl lock-session" },
+ { label: "🚪 Logout", cmd: "hyprctl dispatch exit" },
+ ];
+ powerSub.foreach(c => powerSub.remove(c));
+ for (const entry of entries) {
+ const item = new Gtk.MenuItem({ label: entry.label });
+ item.connect("activate", () => exec(entry.cmd));
+ item.show();
+ powerSub.append(item);
+ }
+ }
+
+ function refreshOutputDevices() {
+ outputSub.foreach(c => outputSub.remove(c));
+ for (const device of audio.get_outputs()) {
+ const label = device.is_default() ? `✔ ${device.get_description()}` : device.get_description();
+ const item = new Gtk.MenuItem({ label });
+ item.connect("activate", () => audio.set_default_output(device));
+ item.show();
+ outputSub.append(item);
+ }
+ }
+
+ function refreshInputDevices() {
+ inputSub.foreach(c => inputSub.remove(c));
+ for (const device of audio.get_inputs()) {
+ const label = device.is_default() ? `✔ ${device.get_description()}` : device.get_description();
+ const item = new Gtk.MenuItem({ label });
+ item.connect("activate", () => audio.set_default_input(device));
+ item.show();
+ inputSub.append(item);
+ }
+ }
+
+ return (
+
+ );
+}
+
+export { createQuickActionsMenu };
diff --git a/config/ags/bar/tsconfig.json b/config/ags/bar/tsconfig.json
index a92bc43..9471e35 100644
--- a/config/ags/bar/tsconfig.json
+++ b/config/ags/bar/tsconfig.json
@@ -9,6 +9,6 @@
// "checkJs": true,
// "allowJs": true,
"jsx": "react-jsx",
- "jsxImportSource": "astal/gtk4",
+ "jsxImportSource": "astal/gtk3",
}
}
diff --git a/config/ags/bar/ui/Bar.tsx b/config/ags/bar/ui/Bar.tsx
index c2db8c5..97be98e 100644
--- a/config/ags/bar/ui/Bar.tsx
+++ b/config/ags/bar/ui/Bar.tsx
@@ -1,36 +1,23 @@
-import { App, Astal, Gtk, Gdk } from "astal/gtk4"
-import { Variable } from "astal"
+import { createQuickActionsMenu } from "./QuickActions";
+import { GLib } from "astal";
+import { Astal, Gdk, Gtk } from "astal/gtk3";
-const time = Variable("").poll(1000, "date")
+const Bar = (gdkmonitor: Gdk.Monitor) => {
-export default function Bar(gdkmonitor: Gdk.Monitor) {
- const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
-
- return
-
-
-
-
-
-
-
-
-
-
-
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
}
+
+export default Bar;
diff --git a/config/ags/bar/ui/QuickActions.tsx b/config/ags/bar/ui/QuickActions.tsx
new file mode 100644
index 0000000..052ce48
--- /dev/null
+++ b/config/ags/bar/ui/QuickActions.tsx
@@ -0,0 +1,76 @@
+import Gtk from "gi://Gtk";
+import AstalNetwork from "gi://AstalNetwork";
+import AstalBluetooth from "gi://AstalBluetooth";
+import { exec } from "gi://GLib";
+
+let quickActionsMenu;
+
+// Toggle WiFi connection
+function toggleWiFi() {
+ const network = AstalNetwork.get_default();
+ const wifi = network.get_wifi()!;
+ const state = wifi.get_state();
+
+ if (state === AstalNetwork.WifiState.DISCONNECTED) {
+ wifi.connect();
+ } else {
+ wifi.disconnect();
+ }
+}
+
+// Toggle Bluetooth power state
+function toggleBluetooth() {
+ const bluetooth = AstalBluetooth.get_default();
+ const adapter = bluetooth.get_adapter();
+ const powered = adapter?.get_powered();
+
+ adapter?.set_powered(!powered);
+}
+
+// Adjust volume or microphone (opens pavucontrol)
+function adjustVolume() {
+ exec("pavucontrol");
+}
+
+function adjustMic() {
+ exec("pavucontrol");
+}
+
+// Power menu
+function showPowerMenu() {
+ exec("power-menu");
+}
+
+// Show WiFi network picker
+function pickWiFi() {
+ const wifi = AstalNetwork.get_default().get_wifi();
+ wifi.show_picker();
+}
+
+// Show Bluetooth device picker
+function pickBluetooth() {
+ const bluetooth = AstalBluetooth.get_default();
+ bluetooth.show_picker();
+}
+
+// Create menu using JSX
+function createQuickActionsMenu() {
+ quickActionsMenu = (
+
+ );
+
+ return quickActionsMenu;
+}
+
+// Export the function
+export { createQuickActionsMenu };
diff --git a/config/ags/bar/ui/widgets/DateTime.tsx b/config/ags/bar/ui/modules/Calendar.tsx
similarity index 100%
rename from config/ags/bar/ui/widgets/DateTime.tsx
rename to config/ags/bar/ui/modules/Calendar.tsx
diff --git a/config/ags/bar/ui/modules/Hyprland.tsx b/config/ags/bar/ui/modules/Hyprland.tsx
new file mode 100644
index 0000000..a08b626
--- /dev/null
+++ b/config/ags/bar/ui/modules/Hyprland.tsx
@@ -0,0 +1,57 @@
+import AstalTray from "gi://AstalTray";
+import { bind } from "astal";
+import AstalHyprland from "gi://AstalHyprland";
+
+const SysTray = () => {
+ const tray = AstalTray.get_default();
+
+ return
+ {bind(tray, "items").as( items => items.map( item => (
+
+ ) ) ) }
+
+}
+
+
+const HyprlandWorkspace = () => {
+ const hypr = AstalHyprland.get_default()
+
+ return
+ {bind(hypr, "workspaces").as(wss => wss
+ .filter(ws => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces
+ .sort((a, b) => a.id - b.id)
+ .map(ws => (
+
+ ))
+ )}
+
+}
+
+
+const HyprlandActiveWindow = () => {
+ const hypr = AstalHyprland.get_default();
+ const focused = bind( hypr, "focusedClient" );
+
+ return
+ {focused.as( client => (
+ client &&
+ ))}
+
+}
+
+export default {
+ HyprlandWorkspace,
+ HyprlandActiveWindow,
+ SysTray
+}
diff --git a/config/ags/bar/ui/widgets/HyprlandActiveWindow.tsx b/config/ags/bar/ui/modules/QuickView.tsx
similarity index 100%
rename from config/ags/bar/ui/widgets/HyprlandActiveWindow.tsx
rename to config/ags/bar/ui/modules/QuickView.tsx
diff --git a/config/ags/bar/ui/widgets/HyprlandWorkspace.tsx b/config/ags/bar/ui/modules/SystemInfo.tsx
similarity index 100%
rename from config/ags/bar/ui/widgets/HyprlandWorkspace.tsx
rename to config/ags/bar/ui/modules/SystemInfo.tsx
diff --git a/config/ags/bar/ui/quickactions.scss b/config/ags/bar/ui/quickactions.scss
new file mode 100644
index 0000000..dc4a40e
--- /dev/null
+++ b/config/ags/bar/ui/quickactions.scss
@@ -0,0 +1,37 @@
+@import "colors";
+
+.toggle-row {
+ background-color: $bg;
+ border-radius: 12px;
+ margin: 6px 0;
+ overflow: hidden;
+ border: 1px solid $border;
+
+ button {
+ padding: 10px 16px;
+ font-size: 14px;
+ transition: background 0.2s ease;
+ border: none;
+ background: transparent;
+
+ &:hover {
+ background-color: $hover;
+ }
+ }
+
+ .toggle {
+ flex: 1;
+ background-color: transparent;
+ text-align: left;
+ &.active {
+ background-color: $accent;
+ color: white;
+ }
+ }
+
+ .arrow {
+ width: 40px;
+ background-color: transparent;
+ text-align: center;
+ }
+}
diff --git a/config/ags/bar/ui/widgets/SystemResources.tsx b/config/ags/bar/ui/widgets/SystemResources.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/ags/bar/ui/widgets/SystemTray.tsx b/config/ags/bar/ui/widgets/SystemTray.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/ags/bar/ui/widgets/resources.tsx b/config/ags/bar/ui/widgets/resources.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/ags/bar/util/hyprland.ts b/config/ags/bar/util/hyprland.ts
new file mode 100644
index 0000000..47a1481
--- /dev/null
+++ b/config/ags/bar/util/hyprland.ts
@@ -0,0 +1,68 @@
+import AstalHyprland from "gi://AstalHyprland";
+
+const getAvailableWorkspaces = (): number[] => {
+ const workspaces: number[] = [];
+ AstalHyprland.get_default().get_workspaces().forEach( val => {
+ workspaces.push( val.get_id() );
+ } )
+ return workspaces;
+}
+
+const getCurrentWorkspaceID = (): number => {
+ return AstalHyprland.get_default().get_focused_workspace().get_id();
+}
+
+const getCurrentWindowTitle = (): string => {
+ return AstalHyprland.get_default().get_focused_client().get_title();
+}
+
+/**
+ * Get the workspace ID of a window by its address
+ * @param address - The address of the window
+ * @returns The workspace ID
+ */
+const getWorkspaceIDOfWindowByAddress = ( address: string ): number => {
+ AstalHyprland.get_default().get_clients().forEach( client => {
+ if ( client.get_address() === address ) {
+ return client.get_workspace().get_id();
+ }
+ } );
+
+ return -1;
+}
+
+
+interface HyprlandSubscriptions {
+ [key: string]: ( data: string ) => void;
+}
+const hooks: HyprlandSubscriptions = {};
+
+/**
+ * Add an event listener for Hyprland events.
+ * @param event - A hyprland IPC event. See https://wiki.hyprland.org/IPC/. Useful events include: urgent, windowtitlev2, workspace, createworkspacev2, destroyworkspacev2, activewindowv2
+ * @param cb - The callback function
+ */
+const subscribeToUpdates = ( event: string, cb: ( data: string ) => void ): void => {
+ hooks[ event ] = cb;
+}
+
+/**
+ * Listen to events. Must be called at some point if events are to be listened for
+ */
+const listen = () => {
+ AstalHyprland.get_default().connect( "event", ( name: string, data: string ) => {
+ if ( hooks[ name ] ) {
+ hooks[ name ]( data );
+ }
+ } );
+}
+
+
+export default {
+ getAvailableWorkspaces,
+ getCurrentWorkspaceID,
+ getCurrentWindowTitle,
+ getWorkspaceIDOfWindowByAddress,
+ subscribeToUpdates,
+ listen
+}
diff --git a/config/ags/bar/ui/widgets/QuickActions.tsx b/config/ags/bar/util/sysinfo.ts
similarity index 100%
rename from config/ags/bar/ui/widgets/QuickActions.tsx
rename to config/ags/bar/util/sysinfo.ts
diff --git a/config/ags/quickactions/.gitignore b/config/ags/quickactions/.gitignore
deleted file mode 100644
index 298eb4d..0000000
--- a/config/ags/quickactions/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/
-@girs/
diff --git a/config/ags/quickactions/app.ts b/config/ags/quickactions/app.ts
deleted file mode 100644
index 7e8cc7c..0000000
--- a/config/ags/quickactions/app.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { App } from "astal/gtk4"
-import style from "./style.scss"
-import Bar from "./widget/Bar"
-
-App.start({
- css: style,
- main() {
- App.get_monitors().map(Bar)
- },
-})
diff --git a/config/ags/quickactions/env.d.ts b/config/ags/quickactions/env.d.ts
deleted file mode 100644
index 467c0a4..0000000
--- a/config/ags/quickactions/env.d.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-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/quickactions/package.json b/config/ags/quickactions/package.json
deleted file mode 100644
index 44226f2..0000000
--- a/config/ags/quickactions/package.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "astal-shell",
- "dependencies": {
- "astal": "/usr/share/astal/gjs"
- }
-}
diff --git a/config/ags/quickactions/style.scss b/config/ags/quickactions/style.scss
deleted file mode 100644
index 1d0d3a9..0000000
--- a/config/ags/quickactions/style.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
-$fg-color: #{"@theme_fg_color"};
-$bg-color: #{"@theme_bg_color"};
-
-window.Bar {
- background: transparent;
- color: $fg-color;
- font-weight: bold;
-
- >centerbox {
- background: $bg-color;
- border-radius: 10px;
- margin: 8px;
- }
-
- button {
- border-radius: 8px;
- margin: 2px;
- }
-}
diff --git a/config/ags/quickactions/tsconfig.json b/config/ags/quickactions/tsconfig.json
deleted file mode 100644
index a92bc43..0000000
--- a/config/ags/quickactions/tsconfig.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "experimentalDecorators": true,
- "strict": true,
- "target": "ES2022",
- "module": "ES2022",
- "moduleResolution": "Bundler",
- // "checkJs": true,
- // "allowJs": true,
- "jsx": "react-jsx",
- "jsxImportSource": "astal/gtk4",
- }
-}
diff --git a/config/ags/quickactions/ui/QuickActions.tsx b/config/ags/quickactions/ui/QuickActions.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/ags/quickactions/ui/widgets/ContextMenu.tsx b/config/ags/quickactions/ui/widgets/ContextMenu.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/ags/quickactions/ui/widgets/MediaControls.tsx b/config/ags/quickactions/ui/widgets/MediaControls.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/ags/quickactions/ui/widgets/toggle.tsx b/config/ags/quickactions/ui/widgets/toggle.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/prompts.md b/prompts.md
new file mode 100644
index 0000000..5fb8586
--- /dev/null
+++ b/prompts.md
@@ -0,0 +1,27 @@
+# Bar
+## Attempt 1
+Use Astal (https://aylur.github.io/astal) to write me a new status bar for hyprland. You may use JSX (be aware, it's not react, but gjs based and has therefore some limitations), but I prefer using the other syntax that is similar to what it was for AGS (the old version of astal), which more closely resembles GTK syntax. The bar should include the following, from left to right:
+Left aligned
+- Date & Time (with seconds, preferably)
+- System stats (i.e. CPU, Memory util, Screen Brightness & Battery status, if available)
+- Workspace number
+Centered
+- The window name
+Right aligned (still left to right)
+- System tray
+- QuickAction menu in GNOME QuickAction menu style, but only the closed version showing icons for volume, mic, WiFi / Ethernet and a Power icon. For the icons (all throughout the bar) use the fluentui-icons (so the Windows 11 icons). If you can't provide them here, tell me what icon should go there and I will put it there manually
+
+The actual QuickAction Menu (which you could also write in a separate file) will provide options where I can pick WiFi, Bluetooth (also turn it on and off), change volume of mic and output, pick the mic and output, have media controls and have a power menu).
+
+For the QuickAction menu, provide a function that is exposed from the file to open and close it, as for all the features (like enabling BT, WiFi, etc).
+
+
+### Followups
+To the WiFi and Bluetooth menus, add the option to pick a WiFi Network / Bluetooth device. Please also fully extract the QuickActions menu to a separate file.
+
+
+Now, can you also provide scss for the bar, such that:
+- I have an easy way to customize colours (which I will be doing using one of my scripts, so having a separate colour config file will be a great option)
+- it is very rounded (the corners)
+- it has a very sleek, but modern design, with only a single accent colour
+- Hovering over something clickable uses a hover colour