diff --git a/flake.nix b/flake.nix
index bc26fb4..04dd0b7 100644
--- a/flake.nix
+++ b/flake.nix
@@ -30,6 +30,7 @@
network
notifd
wireplumber
+ cava
];
extraPackages =
diff --git a/style/_bar.scss b/style/_bar.scss
index f2e10f7..3d1c30d 100644
--- a/style/_bar.scss
+++ b/style/_bar.scss
@@ -104,4 +104,8 @@ window.Bar {
border-radius: 10px;
}
}
+
+ .cava {
+ color: $accent;
+ }
}
diff --git a/widgets/bar/cava.tsx b/widgets/bar/cava.tsx
new file mode 100644
index 0000000..d81acf5
--- /dev/null
+++ b/widgets/bar/cava.tsx
@@ -0,0 +1,49 @@
+import AstalCava from "gi://AstalCava";
+import { createState } from "ags";
+
+const blocks = [
+ "\u2581",
+ "\u2582",
+ "\u2583",
+ "\u2584",
+ "\u2585",
+ "\u2586",
+ "\u2587",
+ "\u2588",
+];
+
+const CAVA_BARS = 14;
+
+export const Cava = () => {
+ const cava = AstalCava.get_default()!;
+ cava.set_bars(CAVA_BARS);
+
+ const [visible, setVisible] = createState(false);
+ const [visuals, setVisuals] = createState("");
+
+ cava.connect("notify::values", ({ values }) => {
+ const isVisible = shouldVisualize(CAVA_BARS, values);
+ if (isVisible) {
+ setVisible(true);
+ setVisuals(
+ values
+ .map(
+ (val) => blocks[Math.min(Math.floor(val * 8), blocks.length - 1)],
+ )
+ .join(""),
+ );
+ } else {
+ setVisible(false);
+ }
+ });
+
+ return (
+
+
+
+ );
+};
+
+const shouldVisualize = (bars: number, values: number[]): boolean => {
+ return !(bars === 0 || values.length === 0 || values.every((v) => v < 0.001));
+};
diff --git a/widgets/bar/index.tsx b/widgets/bar/index.tsx
index b17aab7..10cdba1 100644
--- a/widgets/bar/index.tsx
+++ b/widgets/bar/index.tsx
@@ -7,6 +7,7 @@ import { Time } from "./time";
import { Tray } from "./tray";
import { WorkspaceButton } from "./workspace";
import { Bluetooth } from "./bluetooth";
+import { Cava } from "./cava";
export const WINDOW_NAME = "bar";
@@ -32,7 +33,9 @@ export const Bar = (gdkmonitor: Gdk.Monitor) => {
+
+