diff --git a/app.ts b/app.ts
index a214edc..36b53f5 100644
--- a/app.ts
+++ b/app.ts
@@ -1,6 +1,6 @@
import { App } from "astal/gtk4";
import style from "./style.scss";
-import Bar from "@/widget/Bar";
+import Bar from "@/components/Bar";
App.start({
css: style,
diff --git a/src/components/Bar/index.tsx b/src/components/Bar/index.tsx
new file mode 100644
index 0000000..17b6b44
--- /dev/null
+++ b/src/components/Bar/index.tsx
@@ -0,0 +1,31 @@
+import { Astal, Gtk, Gdk } from "astal/gtk4";
+import QuickSettings from "@/components/QuickSettings";
+import { Workspaces } from "./widgets/workspaces";
+import { Time } from "./widgets/time";
+
+export default function Bar(monitor: Gdk.Monitor) {
+ const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Bar/widgets/time.tsx b/src/components/Bar/widgets/time.tsx
new file mode 100644
index 0000000..b4b3b35
--- /dev/null
+++ b/src/components/Bar/widgets/time.tsx
@@ -0,0 +1,20 @@
+import { Variable, GLib } from "astal";
+import { Gtk } from "astal/gtk4";
+
+export function Time({ format = "%H:%M - %A %e." }) {
+ const time = Variable("").poll(
+ 1000,
+ () => GLib.DateTime.new_now_local().format(format)!,
+ );
+
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/Bar/widgets/workspaces.tsx b/src/components/Bar/widgets/workspaces.tsx
new file mode 100644
index 0000000..9842db6
--- /dev/null
+++ b/src/components/Bar/widgets/workspaces.tsx
@@ -0,0 +1,25 @@
+import { bind } from "astal";
+import Hyprland from "gi://AstalHyprland";
+import Gtk from "gi://Gtk";
+
+export function Workspaces() {
+ const hypr = Hyprland.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) => (
+
+ )),
+ )}
+
+ );
+}
diff --git a/src/components/QuickSettings/index.tsx b/src/components/QuickSettings/index.tsx
new file mode 100644
index 0000000..d47d844
--- /dev/null
+++ b/src/components/QuickSettings/index.tsx
@@ -0,0 +1,39 @@
+import { Gtk } from "astal/gtk4";
+import { WifiStatus, WifiToggle } from "./widgets/wifi";
+import { AudioStatus, AudioSlider } from "./widgets/audio";
+import { BatteryStatus } from "./widgets/battery";
+import { IdleInhibitor } from "./widgets/idle";
+import { BluetoothToggle } from "./widgets/bluetooth";
+import { BrightnessSlider } from "./widgets/brightness";
+
+export default function QuickSettings() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/QuickSettings/widgets/audio.tsx b/src/components/QuickSettings/widgets/audio.tsx
new file mode 100644
index 0000000..ccd609b
--- /dev/null
+++ b/src/components/QuickSettings/widgets/audio.tsx
@@ -0,0 +1,28 @@
+import { bind } from "astal";
+import Wp from "gi://AstalWp";
+
+const speaker = Wp.get_default()?.audio.defaultSpeaker!;
+
+export function AudioStatus() {
+ return (
+
+
+
+ );
+}
+
+export function AudioSlider() {
+ return (
+
+
+ {
+ speaker.volume = value;
+ }}
+ value={bind(speaker, "volume")}
+ />
+
+ );
+}
diff --git a/src/components/QuickSettings/widgets/battery.tsx b/src/components/QuickSettings/widgets/battery.tsx
new file mode 100644
index 0000000..be86963
--- /dev/null
+++ b/src/components/QuickSettings/widgets/battery.tsx
@@ -0,0 +1,15 @@
+import { bind } from "astal";
+import Battery from "gi://AstalBattery";
+
+export function BatteryStatus() {
+ const bat = Battery.get_default();
+
+ return (
+
+
+
+ );
+}
diff --git a/src/components/QuickSettings/widgets/bluetooth.tsx b/src/components/QuickSettings/widgets/bluetooth.tsx
new file mode 100644
index 0000000..5fd39fb
--- /dev/null
+++ b/src/components/QuickSettings/widgets/bluetooth.tsx
@@ -0,0 +1,18 @@
+import { Gtk } from "astal/gtk4";
+import { bind } from "astal";
+import Bluetooth from "gi://AstalBluetooth";
+
+const bluetooth = Bluetooth.get_default();
+
+export function BluetoothToggle() {
+ return (
+
+
+ );
+}
diff --git a/src/components/QuickSettings/widgets/brightness.tsx b/src/components/QuickSettings/widgets/brightness.tsx
new file mode 100644
index 0000000..3df6253
--- /dev/null
+++ b/src/components/QuickSettings/widgets/brightness.tsx
@@ -0,0 +1,20 @@
+import { bind } from "astal";
+import Brightness from "@/services/brightness";
+
+const brightness = Brightness.get_default();
+
+export function BrightnessSlider() {
+ return (
+
+
+ {
+ brightness.screen = value;
+ }}
+ value={bind(brightness, "screen")}
+ />
+
+ );
+}
diff --git a/src/components/QuickSettings/widgets/idle.tsx b/src/components/QuickSettings/widgets/idle.tsx
new file mode 100644
index 0000000..9e73c51
--- /dev/null
+++ b/src/components/QuickSettings/widgets/idle.tsx
@@ -0,0 +1,41 @@
+import { Variable, bind, exec } from "astal";
+import { dependencies } from "@/lib/utils";
+
+type IdleState = "active" | "inactive" | "unknown";
+export function IdleInhibitor() {
+ /*
+ * matcha needs additional checking to ensure the daemon is properly running
+ */
+ function isDaemonRunning() {
+ try {
+ exec("matcha --status");
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ if (!dependencies("matcha") || !isDaemonRunning()) return <>>;
+
+ const state = Variable("unknown");
+
+ function toggle() {
+ exec("matcha --toggle");
+ const response = exec("matcha --status");
+ const enabled = response.match(/on/g);
+ state.set(enabled ? "active" : "inactive");
+ }
+
+ return (
+
+
+ );
+}
diff --git a/src/components/QuickSettings/widgets/wifi.tsx b/src/components/QuickSettings/widgets/wifi.tsx
new file mode 100644
index 0000000..279af41
--- /dev/null
+++ b/src/components/QuickSettings/widgets/wifi.tsx
@@ -0,0 +1,34 @@
+import { bind } from "astal";
+import Network from "gi://AstalNetwork";
+
+const network = Network.get_default();
+const wifi = bind(network, "wifi");
+
+export function WifiStatus() {
+ return (
+
+ {wifi.as(
+ (wifi) =>
+ wifi && (
+
+ ),
+ )}
+
+ );
+}
+
+export function WifiToggle() {
+ return (
+
+ {wifi.as(
+ (wifi) =>
+ wifi && (
+
+ );
+}
diff --git a/src/service/brightness.ts b/src/services/brightness.ts
similarity index 100%
rename from src/service/brightness.ts
rename to src/services/brightness.ts
diff --git a/src/widget/Bar.tsx b/src/widget/Bar.tsx
deleted file mode 100644
index 52109ad..0000000
--- a/src/widget/Bar.tsx
+++ /dev/null
@@ -1,242 +0,0 @@
-import { Astal, Gtk, Gdk } from "astal/gtk4";
-import { Variable, GLib, bind, exec } from "astal";
-import Battery from "gi://AstalBattery";
-import Bluetooth from "gi://AstalBluetooth";
-import Wp from "gi://AstalWp";
-import Network from "gi://AstalNetwork";
-import Hyprland from "gi://AstalHyprland";
-import Brightness from "@/service/brightness";
-import { dependencies } from "@/lib/utils";
-
-const network = Network.get_default();
-const wifi = bind(network, "wifi");
-const hypr = Hyprland.get_default();
-const bluetooth = Bluetooth.get_default();
-
-function Wifi() {
- return (
-
- {wifi.as(
- (wifi) =>
- wifi && (
-
- ),
- )}
-
- );
-}
-
-function Audio() {
- const speaker = Wp.get_default()?.audio.defaultSpeaker!;
-
- return (
-
-
-
- );
-}
-
-function AudioSlider() {
- const speaker = Wp.get_default()?.audio.defaultSpeaker!;
-
- return (
-
-
- {
- speaker.volume = value;
- }}
- value={bind(speaker, "volume")}
- />
-
- );
-}
-
-function BrightnessSlider() {
- const brightness = Brightness.get_default();
-
- return (
-
-
- {
- brightness.screen = value;
- }}
- value={bind(brightness, "screen")}
- />
-
- );
-}
-
-function BatteryLevel() {
- const bat = Battery.get_default();
-
- return (
-
-
-
- );
-}
-
-function Time({ format = "%H:%M - %A %e." }) {
- const time = Variable("").poll(
- 1000,
- () => GLib.DateTime.new_now_local().format(format)!,
- );
-
- return (
-
-
-
-
-
-
-
-
- );
-}
-
-type IdleState = "active" | "inactive" | "unknown";
-
-function IdleInhibitor() {
- /*
- * matcha needs additional checking to ensure the daemon is properly running
- */
- function isDaemonRunning() {
- try {
- exec("matcha --status");
- return true;
- } catch {
- return false;
- }
- }
-
- if (!dependencies("matcha") || !isDaemonRunning()) return <>>;
-
- const state = Variable("unknown");
-
- function toggle() {
- exec("matcha --toggle");
- const response = exec("matcha --status");
- const enabled = response.match(/on/g);
- state.set(enabled ? "active" : "inactive");
- }
-
- return (
-
-
- );
-}
-function QuickSettings() {
- return (
-
-
-
-
-
-
-
-
-
-
-
- {wifi.as(
- (wifi) =>
- wifi && (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-function Workspaces() {
- 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) => (
-
- )),
- )}
-
- );
-}
-export default function Bar(monitor: Gdk.Monitor) {
- const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/widget/Workspaces.tsx b/src/widget/Workspaces.tsx
deleted file mode 100644
index 61ce420..0000000
--- a/src/widget/Workspaces.tsx
+++ /dev/null
@@ -1,2 +0,0 @@
-import { bind } from "astal";
-import Hyprland from "gi://AstalHyprland";