diff --git a/config/astal/app.ts b/config/astal/app.ts
index 6a02a3f..856547e 100644
--- a/config/astal/app.ts
+++ b/config/astal/app.ts
@@ -1,21 +1,23 @@
import { App } from "astal/gtk4"
import style from "./style.scss"
-
-// import notifications from "./components/notifications/handler";
-import Bar from "./components/bar/ui/Bar";
+import Bar from "./components/bar/Bar";
+import AstalHyprland from "gi://AstalHyprland?version=0.1";
App.start({
instanceName: "runner",
css: style,
main() {
- // notifications.startNotificationHandler( App.get_monitors()[0] );
- // TODO: Monitor handling
- Bar.Bar( App.get_monitors()[0] );
+ const hypr = AstalHyprland.get_default();
+ const monitors = App.get_monitors();
+ for (let index = 0; index < monitors.length; index++) {
+ Bar.Bar( monitors[ index ] );
+ }
+
+ // TODO: Handle monitor add
},
requestHandler(request, res) {
const args = request.trimStart().split( ' ' );
- // Notifications (TODO: Handle the arguments in the components themselves)
if ( args[ 0 ] === 'notifier' ) {
res( 'Not available here yet, run astal -i notifier ' + args[ 1 ] );
// res( notifications.cliHandler( args ) );
diff --git a/config/astal/btconf b/config/astal/btconf
new file mode 100644
index 0000000..02e4a84
--- /dev/null
+++ b/config/astal/btconf
@@ -0,0 +1 @@
+false
\ No newline at end of file
diff --git a/config/astal/components/QuickActions/QuickActions.tsx b/config/astal/components/QuickActions/QuickActions.tsx
new file mode 100644
index 0000000..dd97f62
--- /dev/null
+++ b/config/astal/components/QuickActions/QuickActions.tsx
@@ -0,0 +1,74 @@
+import { Gtk } from "astal/gtk4";
+import Power from "./modules/Power";
+import Audio from "./modules/Audio/Audio";
+import Bluetooth from "./modules/Bluetooth/Bluetooth";
+import Brightness from "./modules/Brightness/Brightness";
+import Player from "./modules/Player/Player";
+import { BatteryBox } from "./modules/Battery";
+import { exec } from "astal";
+import Network from "./modules/Networking/Network";
+
+const QuickActions = () => {
+ const popover = new Gtk.Popover({ cssClasses: ["quick-actions-wrapper"] });
+ popover.set_child(renderQuickActions());
+ return popover;
+};
+
+const renderQuickActions = () => {
+ const user = exec("/bin/sh -c whoami");
+ const profile = exec("/bin/fish -c get-profile-picture");
+ const cwd = exec("pwd");
+ const um = Power.UserMenu();
+
+ return (
+
+ um.popup()}
+ cssClasses={["stealthy-button"]}
+ child={
+
+ {um}
+
+ }
+ >
+
+
+ }
+ >
+ }
+ endWidget={
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+// TODO: Expose additional functions to be usable through CLI
+export default {
+ QuickActions,
+};
diff --git a/config/astal/components/QuickActions/dump b/config/astal/components/QuickActions/dump
new file mode 100644
index 0000000..6305c6f
--- /dev/null
+++ b/config/astal/components/QuickActions/dump
@@ -0,0 +1,35 @@
+import { Gtk } from "astal/gtk4";
+import Power from "./modules/Power";
+import Audio from "./modules/Audio/Audio";
+import Bluetooth from "./modules/Bluetooth/Bluetooth";
+import Brightness from "./modules/Brightness/Brightness";
+import Player from "./modules/Player/Player";
+import { BatteryBox } from "./modules/Battery";
+
+const QuickActions = () => {
+ const popover = new Gtk.Overlay( { cssClasses: [ 'quick-actions-wrapper' ] } );
+ popover.set_child(renderQuickActions());
+ return popover;
+};
+
+const renderQuickActions = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+// TODO: Expose additional functions to be usable through CLI
+export default {
+ QuickActions,
+};
diff --git a/config/astal/components/QuickActions/modules/Audio/Audio.scss b/config/astal/components/QuickActions/modules/Audio/Audio.scss
new file mode 100644
index 0000000..2d35eb1
--- /dev/null
+++ b/config/astal/components/QuickActions/modules/Audio/Audio.scss
@@ -0,0 +1,3 @@
+.audio-box {
+ min-width: 320px;
+}
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Audio/Audio.tsx b/config/astal/components/QuickActions/modules/Audio/Audio.tsx
similarity index 93%
rename from config/astal/components/bar/ui/QuickActions/modules/Audio/Audio.tsx
rename to config/astal/components/QuickActions/modules/Audio/Audio.tsx
index de5a95f..3f2d4b4 100644
--- a/config/astal/components/bar/ui/QuickActions/modules/Audio/Audio.tsx
+++ b/config/astal/components/QuickActions/modules/Audio/Audio.tsx
@@ -18,13 +18,14 @@ const AudioModule = () => {
return (
-
+
@@ -42,11 +43,13 @@ const AudioModule = () => {
max={100}
min={0}
step={1}
- widthRequest={100}
+ hexpand
+ vexpand
onChangeValue={self => setVolumeSpeaker(self.value)}
>
-
+
}
+ tooltipText={"View available devices"}
onClicked={() => openBTPicker()}
>
@@ -70,15 +74,44 @@ const openBTPicker = () => {
};
const BluetoothPickerList = () => {
+ let btEnableState = readFile("./btconf") === "true" ? true : false;
+ bt.adapter.set_powered(btEnableState);
+
+ const updateState = () => {
+ btEnableState = !btEnableState;
+ writeFile("./btconf", "" + btEnableState);
+ };
+
return (
- bt.adapter.stop_discovery()}>
-
+ bt.adapter.stop_discovery()}
+ cssClasses={["popover-box"]}
+ >
+
-
+ }
+ endWidget={
+ updateState()}
+ >
+ }
+ >
+
+
+
{bind(bt, "devices").as(devices => {
return devices
.filter(device => {
- if (device.get_connected()) {
+ if (device.get_connected() || device.get_paired()) {
return device;
}
})
@@ -91,13 +124,13 @@ const BluetoothPickerList = () => {
visible={bind(bt, "devices").as(devices => {
return (
devices.filter(device => {
- if (device.get_connected()) {
+ if (device.get_connected() || device.get_paired()) {
return device;
}
}).length === 0
);
})}
- label={"No connected devices"}
+ label={"No connected / trusted devices"}
cssClasses={["bt-no-found", "bt-conn-list"]}
>
- v ? "checkbox" : "paint-unknown-symbolic",
+ iconName={bind(device, "paired").as(v =>
+ v ? "network-bluetooth-activated-symbolic" : "bluetooth-disconnected-symbolic",
)}
>
+
+ v ? "checkbox" : "window-close-symbolic",
+ )}
+ >
+ } onClicked={() => device.set_trusted( !device.get_trusted() )}
+ cssClasses={[ 'button-no-margin' ]}
+ >
}
>
}
onClicked={() => {
- // TODO: Make sure to check if device was previously paired and otherwise do some pairing shenanigans
- device.connect_device( () => {} );
+ connectOrPair( device );
}}
>
);
};
+const connectOrPair = (device: AstalBluetooth.Device) => {
+ if ( device.get_paired() ) {
+ device.connect_device(() => { });
+ // Show failed message if tried to connect and failed
+ } else {
+ device.pair();
+ }
+};
export default BTDevice;
diff --git a/config/astal/components/QuickActions/modules/Brightness/Brightness.tsx b/config/astal/components/QuickActions/modules/Brightness/Brightness.tsx
new file mode 100644
index 0000000..5ddaa36
--- /dev/null
+++ b/config/astal/components/QuickActions/modules/Brightness/Brightness.tsx
@@ -0,0 +1,18 @@
+import { bind } from "astal";
+import Brightness from "../../../../util/brightness";
+
+const brightness = Brightness.get_default();
+
+const BrightnessModule = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default {
+ BrightnessModule
+};
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking-old/Networking.tsx b/config/astal/components/QuickActions/modules/Networking-old/Networking.tsx
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking-old/Networking.tsx
rename to config/astal/components/QuickActions/modules/Networking-old/Networking.tsx
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking-old/README.md b/config/astal/components/QuickActions/modules/Networking-old/README.md
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking-old/README.md
rename to config/astal/components/QuickActions/modules/Networking-old/README.md
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking-old/network.d.ts b/config/astal/components/QuickActions/modules/Networking-old/network.d.ts
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking-old/network.d.ts
rename to config/astal/components/QuickActions/modules/Networking-old/network.d.ts
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking-old/networkinghelper.ts b/config/astal/components/QuickActions/modules/Networking-old/networkinghelper.ts
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking-old/networkinghelper.ts
rename to config/astal/components/QuickActions/modules/Networking-old/networkinghelper.ts
diff --git a/config/astal/components/QuickActions/modules/Networking/Network.tsx b/config/astal/components/QuickActions/modules/Networking/Network.tsx
new file mode 100644
index 0000000..fe16d05
--- /dev/null
+++ b/config/astal/components/QuickActions/modules/Networking/Network.tsx
@@ -0,0 +1,97 @@
+import { bind } from "astal";
+import { Gtk } from "astal/gtk4";
+import AstalNetwork from "gi://AstalNetwork";
+import networkHelper from "./network-helper";
+import NetworkMenu from "./NetworkMenu";
+
+const net = AstalNetwork.get_default();
+const STATE = AstalNetwork.DeviceState;
+
+const Network = () => {
+ const netMenu = NetworkMenu.NetworkMenu();
+ return (
+
+ {
+ if (en) return ["toggle-button", "toggle-on"];
+ else return ["toggle-button"];
+ })}
+ onClicked={() =>
+ networkHelper.setNetworking(
+ !networkHelper.networkEnabled.get(),
+ )
+ }
+ child={
+
+
+
+
+
+ }
+ >
+ netMenu.popup()}
+ child={
+
+
+ { netMenu }
+
+ }
+ tooltipText={"View available devices"}
+ >
+
+ );
+};
+
+export default {
+ Network,
+};
diff --git a/config/astal/components/QuickActions/modules/Networking/NetworkMenu.tsx b/config/astal/components/QuickActions/modules/Networking/NetworkMenu.tsx
new file mode 100644
index 0000000..6a02e31
--- /dev/null
+++ b/config/astal/components/QuickActions/modules/Networking/NetworkMenu.tsx
@@ -0,0 +1,18 @@
+import { Gtk } from "astal/gtk4";
+
+const NetworkMenu = () => {
+ const popover = new Gtk.Popover();
+ popover.set_child( renderMenu() );
+ return popover;
+};
+
+const renderMenu = () => {
+ return
+
+
+ ;
+};
+
+export default {
+ NetworkMenu,
+};
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/Network.tsx b/config/astal/components/QuickActions/modules/Networking/dump
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking/Network.tsx
rename to config/astal/components/QuickActions/modules/Networking/dump
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/network-helper.ts b/config/astal/components/QuickActions/modules/Networking/network-helper.ts
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking/network-helper.ts
rename to config/astal/components/QuickActions/modules/Networking/network-helper.ts
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/network.d.ts b/config/astal/components/QuickActions/modules/Networking/network.d.ts
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking/network.d.ts
rename to config/astal/components/QuickActions/modules/Networking/network.d.ts
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Networking/wifi-helper.ts b/config/astal/components/QuickActions/modules/Networking/wifi-helper.ts
similarity index 100%
rename from config/astal/components/bar/ui/QuickActions/modules/Networking/wifi-helper.ts
rename to config/astal/components/QuickActions/modules/Networking/wifi-helper.ts
diff --git a/config/astal/components/QuickActions/modules/Player/Player.scss b/config/astal/components/QuickActions/modules/Player/Player.scss
new file mode 100644
index 0000000..a26faac
--- /dev/null
+++ b/config/astal/components/QuickActions/modules/Player/Player.scss
@@ -0,0 +1,56 @@
+$fg-color: #{"@theme_fg_color"};
+$bg-color: #{"@theme_bg_color"};
+
+box.players-box {
+ margin-top: 20px;
+}
+
+box.player {
+ padding: 0.6rem;
+
+ .cover-art {
+ min-width: 100px;
+ min-height: 100px;
+ border-radius: 9px;
+ margin-right: 0.6rem;
+ background-size: contain;
+ background-position: center;
+ }
+
+ .title {
+ font-weight: bold;
+ font-size: 1.1em;
+ }
+
+ scale {
+ padding: 0;
+ margin: 0.4rem 0;
+ border-radius: 20px;
+
+ trough {
+ min-height: 8px;
+ border-radius: 20px;
+ }
+
+ highlight {
+ background-color: $fg-color;
+ border-radius: 20px;
+ }
+
+ slider {
+ all: unset;
+ border-radius: 20px;
+ }
+ }
+
+ centerbox.actions {
+ min-width: 220px;
+
+ button {
+ min-width: 0;
+ min-height: 0;
+ padding: 0.4rem;
+ margin: 0 0.2rem;
+ }
+ }
+}
diff --git a/config/astal/components/QuickActions/modules/Player/Player.tsx b/config/astal/components/QuickActions/modules/Player/Player.tsx
new file mode 100644
index 0000000..7e3516b
--- /dev/null
+++ b/config/astal/components/QuickActions/modules/Player/Player.tsx
@@ -0,0 +1,154 @@
+import { bind } from "astal";
+import { Gtk } from "astal/gtk4";
+import AstalMpris from "gi://AstalMpris";
+import Pango from "gi://Pango?version=1.0";
+const ALIGN = Gtk.Align;
+
+const mpris = AstalMpris.get_default();
+mpris.connect("player-added", p => {
+ print("Player added:", p);
+});
+
+const PlayerModule = () => {
+ return (
+
+
+
+
+ {bind(mpris, "players").as(players => {
+ return players.map(player => {
+ return ;
+ });
+ })}
+
+
+
+ );
+};
+
+// TODO: Update widths
+const pbStatus = AstalMpris.PlaybackStatus;
+const PlayerItem = ({ player }: { player: AstalMpris.Player }) => {
+ return (
+
+
+
+
+
+ l > 0)}
+ value={bind(player, "position")}
+ min={0}
+ max={bind(player, "length")}
+ onChangeValue={v =>
+ player.set_position(v.get_value())
+ }
+ >
+
+ secondsToFriendlyTime(v),
+ )}
+ hexpand
+ cssClasses={["position"]}
+ >
+ }
+ centerWidget={
+
+
+ }
+ onClicked={() => player.previous()}
+ >
+ {
+ if (status === pbStatus.PLAYING) {
+ return "media-playback-pause-symbolic";
+ } else {
+ return "media-playback-start-symbolic";
+ }
+ })}
+ >
+ }
+ onClicked={() => player.play_pause()}
+ >
+
+ }
+ onClicked={() => player.next()}
+ >
+
+ }
+ endWidget={
+
+ }
+ >
+
+
+ );
+};
+
+const secondsToFriendlyTime = (time: number) => {
+ const minutes = Math.floor(time / 60);
+ const hours = Math.floor(minutes / 60);
+ const seconds = Math.floor(time % 60);
+ if (hours > 0) {
+ return `${hours}:${expandTime(minutes)}:${expandTime(seconds)}`;
+ } else {
+ return `${minutes}:${expandTime(seconds)}`;
+ }
+};
+
+const expandTime = (time: number): string => {
+ return time < 10 ? `0${time}` : "" + time;
+};
+
+export default {
+ PlayerModule,
+};
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Power.tsx b/config/astal/components/QuickActions/modules/Power.tsx
similarity index 66%
rename from config/astal/components/bar/ui/QuickActions/modules/Power.tsx
rename to config/astal/components/QuickActions/modules/Power.tsx
index 36442c0..5402073 100644
--- a/config/astal/components/bar/ui/QuickActions/modules/Power.tsx
+++ b/config/astal/components/QuickActions/modules/Power.tsx
@@ -12,29 +12,17 @@ const PowerMenu = (): Gtk.Popover => {
child={
}
- onClicked={() => exec("shutdown now")}
+ onClicked={() => exec("/bin/sh -c 'shutdown now'")}
>
}
- onClicked={() => exec("reboot")}
+ onClicked={() => exec("/bin/sh -c 'reboot'")}
>
}
- onClicked={() => exec("systemctl suspend")}
- >
-
- }
- onClicked={() => exec("hyprlock")}
- >
- }
- onClicked={() => exec("hyprctl dispatch exit 0")}
+ onClicked={() => exec("/bin/sh -c 'systemctl suspend'")}
>
);
@@ -48,7 +36,10 @@ const Power = () => {
const pm = PowerMenu();
return (
@@ -60,4 +51,35 @@ const Power = () => {
);
};
-export default Power;
+const UserMenu = (): Gtk.Popover => {
+ const popover = new Gtk.Popover();
+
+ const powerMenuBox = () => {
+ return (
+
+
+ }
+ onClicked={() => exec("/bin/sh -c 'hyprlock'")}
+ >
+ }
+ onClicked={() =>
+ exec("/bin/sh -c 'hyprctl dispatch exit 0'")
+ }
+ >
+
+ );
+ };
+
+ popover.set_child(powerMenuBox());
+ return popover;
+};
+
+export default {
+ Power,
+ UserMenu
+};
diff --git a/config/astal/components/QuickActions/quickactions.scss b/config/astal/components/QuickActions/quickactions.scss
new file mode 100644
index 0000000..8f0302d
--- /dev/null
+++ b/config/astal/components/QuickActions/quickactions.scss
@@ -0,0 +1,56 @@
+@use "./modules/Player/Player.scss";
+@use "./modules/Audio/Audio.scss";
+@use "../../util/colours.scss" as *;
+
+.quick-actions-wrapper {
+ min-width: 520px;
+}
+
+box.quick-actions {
+ padding: 10px;
+}
+
+popover * {
+ border-radius: 20px;
+}
+
+button {
+ margin: 4px;
+}
+
+.button-no-margin {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.devices-list {
+ margin-bottom: 20px;
+}
+
+button.toggle-button {
+ min-width: 220px;
+ border-radius: 50px;
+
+ &.toggle-on {
+ min-width: 190px;
+ margin-right: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ background-color: $accent-color;
+ }
+}
+
+button.actions-button {
+ margin-left: 0;
+ border-radius: 0;
+ background-color: $accent-color;
+ border-top-right-radius: 50px;
+ border-bottom-right-radius: 50px;
+}
+
+.avatar-icon {
+ border-radius: 100px;
+ min-height: 40px;
+ min-width: 40px;
+ margin-right: 10px;
+}
diff --git a/config/astal/components/bar/ui/Bar.tsx b/config/astal/components/bar/Bar.tsx
similarity index 97%
rename from config/astal/components/bar/ui/Bar.tsx
rename to config/astal/components/bar/Bar.tsx
index 1f38694..3a1c273 100644
--- a/config/astal/components/bar/ui/Bar.tsx
+++ b/config/astal/components/bar/Bar.tsx
@@ -23,7 +23,6 @@ const Bar = (gdkmonitor: Gdk.Monitor) => {
diff --git a/config/astal/components/bar/bar.scss b/config/astal/components/bar/bar.scss
new file mode 100644
index 0000000..156653d
--- /dev/null
+++ b/config/astal/components/bar/bar.scss
@@ -0,0 +1,62 @@
+@use "../../util/colours.scss" as *;
+
+window.Bar {
+ font-family: "Comfortaa, sans-serif";
+ background: transparent;
+ color: $fg-color;
+ font-weight: bold;
+
+ /* >centerbox { */
+ /* background: $bg-color; */
+ /* border-radius: 10px; */
+ /* margin: 8px; */
+ /* } */
+
+ .bar-button {
+ border-radius: 20px;
+ margin: 2px;
+ padding-left: 10px;
+ padding-right: 10px;
+ background-color: $bg-color;
+
+ & button {
+ background-color: $bg-color;
+ }
+ }
+
+ .quick-action-button {
+ border-radius: 20px;
+ margin: 2px;
+ padding-left: 10px;
+ padding-right: 10px;
+ background-color: $bg-color;
+ }
+
+ button.workspace-button {
+ border-radius: 20px;
+ margin: 1px;
+
+ &.focused-workspace-button {
+ color: $accent-color-2;
+ }
+ }
+
+ .tray-item {
+ margin: 0;
+ padding: 0;
+
+ & button {
+ margin: 2px;
+ box-shadow: none;
+ }
+ }
+
+ .time {
+ min-width: 11rem;
+ padding: 3px;
+ & button {
+ box-shadow: none;
+ padding: 0;
+ }
+ }
+}
diff --git a/config/astal/components/bar/modules/Calendar.tsx b/config/astal/components/bar/modules/Calendar.tsx
new file mode 100644
index 0000000..db6d06b
--- /dev/null
+++ b/config/astal/components/bar/modules/Calendar.tsx
@@ -0,0 +1,26 @@
+import { GLib, Variable } from "astal";
+import { Gtk } from "astal/gtk4";
+
+const Time = ({ format = "%a, %e.%m %H:%M:%S" }) => {
+ const time = Variable("").poll(
+ 1000,
+ () => GLib.DateTime.new_now_local().format(format)!,
+ );
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default {
+ Time,
+};
diff --git a/config/astal/components/bar/modules/Hyprland.tsx b/config/astal/components/bar/modules/Hyprland.tsx
new file mode 100644
index 0000000..d540b5e
--- /dev/null
+++ b/config/astal/components/bar/modules/Hyprland.tsx
@@ -0,0 +1,197 @@
+import AstalTray from "gi://AstalTray";
+import { bind, GObject } from "astal";
+import AstalHyprland from "gi://AstalHyprland";
+import { Gtk } from "astal/gtk4";
+
+const SYNC = GObject.BindingFlags.SYNC_CREATE;
+
+const SysTray = () => {
+ const trayBox = new Gtk.Box({ cssClasses: ["bar-button"] });
+ const tray = AstalTray.get_default();
+
+ const trayItems = new Map();
+ const trayAddedHandler = tray.connect("item-added", (_, id) => {
+ const item = tray.get_item(id);
+ const popover = Gtk.PopoverMenu.new_from_model(item.menu_model);
+ const icon = new Gtk.Image();
+ const button = new Gtk.MenuButton({
+ popover,
+ child: icon,
+ cssClasses: ["tray-item"],
+ });
+
+ item.bind_property("gicon", icon, "gicon", SYNC);
+ popover.insert_action_group("dbusmenu", item.action_group);
+ item.connect("notify::action-group", () => {
+ popover.insert_action_group("dbusmenu", item.action_group);
+ });
+
+ trayItems.set(id, button);
+ trayBox.append(button);
+ });
+
+ const trayRemovedHandler = tray.connect("item-removed", (_, id) => {
+ const button = trayItems.get(id);
+ if (button) {
+ trayBox.remove(button);
+ button.run_dispose();
+ trayItems.delete(id);
+ }
+ });
+
+ trayBox.connect("destroy", () => {
+ tray.disconnect(trayAddedHandler);
+ tray.disconnect(trayRemovedHandler);
+ });
+
+ return trayBox;
+};
+
+const Workspace = () => {
+ 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 => (
+
+ ws === fw
+ ? [
+ "focused-workspace-button",
+ "workspace-button",
+ ]
+ : ["workspace-button"],
+ )}
+ onButtonPressed={() => ws.focus()}
+ child={}
+ >
+ )),
+ )}
+
+ );
+};
+
+/**
+ * Displays the name of the currently active window and provides a popover for
+ * displaying all available clients
+ */
+const ActiveWindow = () => {
+ const hypr = AstalHyprland.get_default();
+ const focused = bind(hypr, "focusedClient");
+
+ const WindowPopover = (): Gtk.Popover => {
+ // Set up boxes + Popover
+ const clients = new Map();
+ const popover = new Gtk.Popover();
+ const popoverBox = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ });
+
+ const widgetTitle = new Gtk.Label({
+ cssClasses: ["title-2"],
+ label: "Available Windows",
+ });
+
+ popoverBox.append(widgetTitle);
+
+ const seaparator = new Gtk.Separator({
+ marginTop: 5,
+ marginBottom: 10,
+ });
+
+ popoverBox.append(seaparator);
+
+ const addClient = (client: AstalHyprland.Client) => {
+ const clientBox = new Gtk.Box();
+
+ // Workspace description
+ const descWS = new Gtk.Label({ label: "(WS " });
+
+ // Workpsace information
+ const workspace = new Gtk.Label();
+ client.workspace.bind_property("name", workspace, "label", SYNC);
+
+ const windowClassDesc = new Gtk.Label({ label: ") [" });
+
+ const windowClass = new Gtk.Label();
+ windowClass.label = client.get_initial_class();
+
+ const titleDesc = new Gtk.Label({ label: "] " });
+ titleDesc.set_margin_end(2);
+
+ const title = new Gtk.Label();
+ client.bind_property("title", title, "label", SYNC);
+
+ clientBox.append(descWS);
+ clientBox.append(workspace);
+ clientBox.append(windowClassDesc);
+ clientBox.append(windowClass);
+ clientBox.append(titleDesc);
+ clientBox.append(title);
+
+ const button = new Gtk.Button();
+ button.connect( 'clicked', () => {
+ client.workspace.focus();
+ } );
+ button.set_child(clientBox);
+
+ popoverBox.append(button);
+
+ clients.set(client.get_address(), button);
+ };
+
+ // Populate with already added clients
+ const c = hypr.get_clients();
+ for (let index = 0; index < c.length; index++) {
+ addClient(c[index]);
+ }
+
+ hypr.connect("client-added", (_, client) => {
+ addClient(client);
+ });
+
+ hypr.connect("client-removed", (_, client) => {
+ const c = clients.get(client);
+ if (c) {
+ popoverBox.remove(c);
+ c.run_dispose();
+ clients.delete(client);
+ }
+ });
+
+ popover.set_child(popoverBox);
+ return popover;
+ };
+
+ const windowPopover = WindowPopover();
+
+ // ───────────────────────────────────────────────────────────────────
+ // Return fully assembled HyprlandFocusedClient box
+ // ───────────────────────────────────────────────────────────────────
+ return (
+
+ windowPopover.popup()}
+ cssClasses={["bar-button"]}
+ >
+ {focused.as(
+ client =>
+ client && (
+
+ ),
+ )}
+
+ {windowPopover}
+
+ );
+};
+
+export default {
+ Workspace,
+ ActiveWindow,
+ SysTray,
+};
diff --git a/config/astal/components/bar/modules/QuickView.tsx b/config/astal/components/bar/modules/QuickView.tsx
new file mode 100644
index 0000000..f8c8177
--- /dev/null
+++ b/config/astal/components/bar/modules/QuickView.tsx
@@ -0,0 +1,186 @@
+import { bind } from "astal";
+import AstalBattery from "gi://AstalBattery";
+import AstalBluetooth from "gi://AstalBluetooth";
+import AstalNetwork from "gi://AstalNetwork";
+import AstalWp from "gi://AstalWp";
+import { Gtk } from "astal/gtk4";
+import Brightness from "../../../util/brightness";
+import QuickActions from "../../QuickActions/QuickActions";
+
+const STATE = AstalNetwork.DeviceState;
+
+const QuickView = () => {
+ const qa = QuickActions.QuickActions();
+ const showQuickActions = () => {
+ qa.popup();
+ };
+
+ return (
+ showQuickActions()}
+ cssClasses={["quick-action-button"]}
+ child={
+
+
+
+
+
+
+
+ {qa}
+
+ }
+ >
+ );
+};
+
+const NetworkWidget = () => {
+ const network = AstalNetwork.get_default();
+
+ return (
+
+ {
+ if (state === AstalNetwork.State.CONNECTING) {
+ return "chronometer-reset-symbolic";
+ } else if (
+ state === AstalNetwork.State.CONNECTED_LOCAL ||
+ state === AstalNetwork.State.CONNECTED_SITE ||
+ state === AstalNetwork.State.CONNECTED_GLOBAL
+ ) {
+ return "network-wired-activated-symbolic";
+ } else {
+ return "paint-unknown-symbolic";
+ }
+ })}
+ cssClasses={["network-widget", "quick-view-symbol"]}
+ visible={bind(network.wifi, "state").as(
+ state => state !== STATE.ACTIVATED,
+ )}
+ >
+ {
+ if (state === STATE.ACTIVATED) {
+ return network.wifi.iconName;
+ } else {
+ return "";
+ }
+ })}
+ cssClasses={["network-widget", "quick-view-symbol"]}
+ visible={bind(network.wifi, "state").as(
+ state => state === STATE.ACTIVATED,
+ )}
+ >
+
+ );
+};
+
+const BluetoothWidget = () => {
+ const bluetooth = AstalBluetooth.get_default();
+ const enabled = bind(bluetooth.adapter, "powered");
+ const connected = bind(bluetooth, "isConnected");
+
+ // For each connected BT device, render status
+ return (
+
+ e)}>
+ c)}
+ >
+ !c)}
+ >
+
+ !e)}
+ >
+
+ {bind(bluetooth, "devices").as(devices => {
+ return devices.map(device => {
+ return (
+ c)}>
+ icon,
+ )}
+ >
+
+
+ );
+ });
+ })}
+
+
+ );
+};
+
+const BatteryWidget = () => {
+ const battery = AstalBattery.get_default();
+ if (battery.get_is_present()) {
+ return (
+ icon)}
+ cssClasses={["quick-view-symbol"]}
+ >
+ );
+ } else {
+ return ;
+ }
+ // Else, no battery available -> Don't show the widget
+};
+
+const BrightnessWidget = () => {
+ const brightness = Brightness.get_default();
+ const screen_brightness = bind(brightness, "screen");
+
+ return (
+
+ );
+};
+
+const Audio = () => {
+ const wireplumber = AstalWp.get_default();
+ if (wireplumber) {
+ return (
+
+ icon,
+ )}
+ cssClasses={["quick-view-symbol"]}
+ >
+ icon)}
+ cssClasses={["quick-view-symbol"]}
+ >
+
+ );
+ } else {
+ print(
+ "[ WirePlumber ] Could not connect, Audio support in bar will be missing",
+ );
+ return ;
+ }
+};
+
+// cssClasses={[ 'quick-view-symbol' ]}
+
+export default {
+ QuickView,
+};
diff --git a/config/astal/components/bar/modules/SystemInfo.tsx b/config/astal/components/bar/modules/SystemInfo.tsx
new file mode 100644
index 0000000..f63c1f6
--- /dev/null
+++ b/config/astal/components/bar/modules/SystemInfo.tsx
@@ -0,0 +1,211 @@
+import { exec, execAsync, GLib, interval, Variable } from "astal";
+import { Gtk } from "astal/gtk4";
+import AstalBattery from "gi://AstalBattery?version=0.1";
+
+const FETCH_INTERVAL = 2000;
+
+const cpuUtil = Variable("0%");
+const ramUtil = Variable("0%");
+const ramUsed = Variable("0MiB");
+const gpuUtil = Variable("0%");
+let gpuName = "card1";
+let enabled = false;
+
+const refreshStats = (): Stats => {
+ gpuName = exec(`/bin/bash -c "ls /sys/class/drm/ | grep '^card[0-9]*$'"`);
+ const cpuNameInSensors = "CPUTIN";
+ const stats = {
+ kernel: exec("uname -sr"),
+ netSpeed: exec(
+ `/bin/bash -c "interface=$(ip route get 8.8.8.8 | awk '{print $5; exit}') && cat \"/sys/class/net/$interface/speed\""`,
+ ),
+ cpuTemp: exec(
+ `/bin/bash -c "sensors | grep -m1 ${cpuNameInSensors} | awk '{print $2}'"`,
+ ),
+ cpuClk: exec(
+ `awk '/cpu MHz/ {sum+=$4; ++n} END {print sum/n " MHz"}' /proc/cpuinfo`,
+ ),
+ gpuTemp: exec(
+ `/bin/bash -c "sensors | grep -E 'edge' | awk '{print $2}'"`,
+ ),
+ gpuClk: exec(
+ `/bin/bash -c "cat /sys/class/drm/${gpuName}/device/pp_dpm_sclk | grep '\\*' | awk '{print $2 $3}'"`,
+ ),
+ vram:
+ Math.round(
+ parseInt(
+ exec(
+ `cat /sys/class/drm/${gpuName}/device/mem_info_vram_used`,
+ ),
+ ) /
+ 1024 /
+ 1024,
+ ) + "MiB",
+ availableVRAM:
+ Math.round(
+ parseInt(
+ exec(
+ `cat /sys/class/drm/${gpuName}/device/mem_info_vram_total`,
+ ),
+ ) /
+ 1024 /
+ 1024,
+ ) + "MiB",
+ };
+
+ return stats;
+};
+
+const systemStats: Variable = Variable(refreshStats());
+
+const availableFeatures = {
+ cpu: true,
+ ram: true,
+};
+
+const featureTest = () => {
+ // Check if awk & sed are available
+ try {
+ exec("awk -V");
+ exec("sed --version");
+ enabled = true;
+ } catch (e) {
+ printerr(
+ "[ SysInfo ] AWK or SED missing! No system info will be available",
+ );
+ enabled = false;
+ return;
+ }
+
+ // Check if mpstat is available
+ try {
+ exec("mpstat -V");
+ } catch (e) {
+ availableFeatures.cpu = false;
+ printerr(
+ "[ SysInfo ] Feature Test for CPU info failed. mpstat from the sysstat package missing!",
+ );
+ }
+};
+
+const info = () => {
+ return (
+
+
+
+
+
+
+ exec( `/bin/sh -c "kitty --hold fish -c 'fastfetch'"` )}
+ child={
+
+ }>
+
+ );
+};
+
+const SystemInformationPanel = () => {
+ const popover = new Gtk.Popover();
+
+ popover.set_child(info());
+
+ return popover;
+};
+
+const sysInfoFetcher = () => {
+ if (enabled) {
+ if (availableFeatures.cpu) {
+ cpuUtil.set(
+ "" +
+ Math.round(
+ parseFloat(exec(`/bin/fish -c cpu-utilization`)),
+ ),
+ );
+ }
+ if (availableFeatures.ram) {
+ ramUtil.set(
+ "" +
+ Math.round(
+ parseFloat(
+ exec(
+ `/bin/bash -c "free | awk '/Mem/ { printf(\\"%.2f\\\\n\\", ($3/$2)*100) }'"`,
+ ),
+ ),
+ ),
+ );
+ ramUsed.set(
+ exec(
+ `/bin/bash -c \"free -h | awk '/^Mem:/ {print $3 \\" used of \\" $2}'\"`,
+ )
+ .replaceAll("Gi", "GiB")
+ .replaceAll("Mi", "MiB"),
+ );
+ }
+ gpuUtil.set(exec("cat /sys/class/drm/card1/device/gpu_busy_percent"));
+ }
+};
+
+const panel = SystemInformationPanel();
+
+const SystemInfo = () => {
+ featureTest();
+
+ const openSysInfo = async () => {
+ panel.popup();
+ systemStats.set(refreshStats());
+ };
+
+ if (enabled) {
+ sysInfoFetcher();
+ interval(FETCH_INTERVAL, sysInfoFetcher);
+
+ return (
+ openSysInfo()}
+ child={
+ v)}>
+
+
+
+
+
+
+ {panel}
+
+ }
+ cssClasses={["bar-button"]}
+ >
+ );
+ } else {
+ return ;
+ }
+};
+
+export default {
+ SystemInfo,
+ panel,
+};
diff --git a/config/astal/components/bar/ui/modules/stats.d.ts b/config/astal/components/bar/modules/stats.d.ts
similarity index 100%
rename from config/astal/components/bar/ui/modules/stats.d.ts
rename to config/astal/components/bar/modules/stats.d.ts
diff --git a/config/astal/components/bar/ui/QuickActions/QuickActions.tsx b/config/astal/components/bar/ui/QuickActions/QuickActions.tsx
deleted file mode 100644
index 096af5f..0000000
--- a/config/astal/components/bar/ui/QuickActions/QuickActions.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Gtk } from "astal/gtk4"
-import Power from "./modules/Power";
-import Audio from "./modules/Audio/Audio";
-import Bluetooth from "./modules/Bluetooth/Bluetooth";
-
-const QuickActions = () => {
- const popover = new Gtk.Popover( { cssClasses: [ 'quick-actions-popover' ] } );
-
- popover.set_child( createQuickActionMenu() );
-
- return popover;
-}
-
-
-const createQuickActionMenu = () => {
- // TODO: For the future add WiFi / Networking back, for the time being remove, as unnecessary effort
- return
-
-
-
-
-}
-
-
-// TODO: Expose additional functions to be usable through CLI
-export default {
- QuickActions
-};
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Bluetooth/bthelper.ts b/config/astal/components/bar/ui/QuickActions/modules/Bluetooth/bthelper.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Brightness/Brightness.tsx b/config/astal/components/bar/ui/QuickActions/modules/Brightness/Brightness.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/astal/components/bar/ui/QuickActions/modules/Player/Player.tsx b/config/astal/components/bar/ui/QuickActions/modules/Player/Player.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/config/astal/components/bar/ui/QuickActions/quickactions.scss b/config/astal/components/bar/ui/QuickActions/quickactions.scss
deleted file mode 100644
index bc4a9b1..0000000
--- a/config/astal/components/bar/ui/QuickActions/quickactions.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.title-2 {
- font-size: 1.2rem;
- font-weight: bold;
-}
-
-.bt-conn-list {
- margin-bottom: 20px;
-}
-
-popover>box {
- margin: 10px;
- border-radius: 50px;
-}
diff --git a/config/astal/components/bar/ui/bar.scss b/config/astal/components/bar/ui/bar.scss
deleted file mode 100644
index 7b19d10..0000000
--- a/config/astal/components/bar/ui/bar.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-$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/astal/components/bar/ui/modules/Calendar.tsx b/config/astal/components/bar/ui/modules/Calendar.tsx
deleted file mode 100644
index abe16c7..0000000
--- a/config/astal/components/bar/ui/modules/Calendar.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { GLib, Variable } from "astal"
-import { Gtk } from "astal/gtk4"
-
-const Time = ({ format = "%a, %e.%m %H:%M:%S" }) => {
- const time = Variable("").poll(1000, () =>
- GLib.DateTime.new_now_local().format(format)!)
-
- return
-
-
-
-
-
-}
-
-const Calendar = () => {
-
-}
-
-
-export default {
- Time
-}
diff --git a/config/astal/components/bar/ui/modules/Hyprland.tsx b/config/astal/components/bar/ui/modules/Hyprland.tsx
deleted file mode 100644
index 0933a78..0000000
--- a/config/astal/components/bar/ui/modules/Hyprland.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-
-import AstalTray from "gi://AstalTray";
-import { bind, GObject } from "astal";
-import AstalHyprland from "gi://AstalHyprland";
-import { Gtk } from "astal/gtk4";
-
-const SYNC = GObject.BindingFlags.SYNC_CREATE;
-
-const SysTray = () => {
- const trayBox = new Gtk.Box();
- const tray = AstalTray.get_default();
-
- const trayItems = new Map();
- const trayAddedHandler = tray.connect("item-added", (_, id) => {
- const item = tray.get_item(id);
- const popover = Gtk.PopoverMenu.new_from_model(item.menu_model);
- const icon = new Gtk.Image();
- const button = new Gtk.MenuButton({ popover, child: icon });
-
- item.bind_property("gicon", icon, "gicon", SYNC);
- popover.insert_action_group("dbusmenu", item.action_group);
- item.connect("notify::action-group", () => {
- popover.insert_action_group("dbusmenu", item.action_group);
- });
-
- trayItems.set(id, button);
- trayBox.append(button);
- })
-
- const trayRemovedHandler = tray.connect("item-removed", (_, id) => {
- const button = trayItems.get(id);
- if (button) {
- trayBox.remove(button);
- button.run_dispose();
- trayItems.delete(id);
- }
- });
-
- trayBox.connect("destroy", () => {
- tray.disconnect(trayAddedHandler);
- tray.disconnect(trayRemovedHandler);
- });
-
- return trayBox;
-}
-
-
-const Workspace = () => {
- 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 => (
-
- ws === fw ? ["HyprlandFocusedWorkspace"] : [""])}
- onButtonPressed={() => ws.focus()} child={}>
-
- ))
- )}
-
-}
-
-
-/**
- * Displays the name of the currently active window and provides a popover for
- * displaying all available clients
- */
-const ActiveWindow = () => {
- const hypr = AstalHyprland.get_default();
- const focused = bind( hypr, "focusedClient" );
-
- const WindowPopover = (): Gtk.Popover => {
- // Set up boxes + Popover
- const clients = new Map();
- const popover = new Gtk.Popover();
- const popoverBox = new Gtk.Box( { orientation: Gtk.Orientation.VERTICAL } );
-
- const addClient = ( client: AstalHyprland.Client ) => {
- const clientBox = new Gtk.Box();
-
- // Workspace description
- const descWS = new Gtk.Label( { label: '(WS ' } );
-
- // Workpsace information
- const workspace = new Gtk.Label();
- client.workspace.bind_property( 'name', workspace, 'label', SYNC );
-
- const windowClassDesc = new Gtk.Label( { label: ') [' } );
-
- const windowClass = new Gtk.Label();
- windowClass.label = client.get_initial_class();
-
- const titleDesc = new Gtk.Label( { label: '] ' } );
- titleDesc.set_margin_end( 2 );
-
- const title = new Gtk.Label();
- client.bind_property( 'title', title, 'label', SYNC );
-
- clientBox.append( descWS );
- clientBox.append( workspace );
- clientBox.append( windowClassDesc );
- clientBox.append( windowClass );
- clientBox.append( titleDesc );
- clientBox.append( title );
-
- popoverBox.append( clientBox );
-
- clients.set( client.get_address(), clientBox );
- }
-
- // Populate with already added clients
- const c = hypr.get_clients();
- for ( let index = 0; index < c.length; index++ ) {
- addClient( c[ index ] );
- }
-
- hypr.connect( 'client-added', ( _, client ) => {
- addClient( client );
- } );
-
- hypr.connect( 'client-removed', ( _, client ) => {
- const c = clients.get( client );
- if ( c ) {
- popoverBox.remove( c );
- c.run_dispose();
- clients.delete( client );
- }
- } );
-
- popover.set_child( popoverBox );
- return popover;
- }
-
- const windowPopover = WindowPopover();
-
- // ───────────────────────────────────────────────────────────────────
- // Return fully assembled HyprlandFocusedClient box
- // ───────────────────────────────────────────────────────────────────
- return
- windowPopover.popup()}>
- {focused.as( client => (
- client &&
- ))}
-
- { windowPopover }
-
-}
-
-
-export default {
- Workspace,
- ActiveWindow,
- SysTray
-}
diff --git a/config/astal/components/bar/ui/modules/QuickView.tsx b/config/astal/components/bar/ui/modules/QuickView.tsx
deleted file mode 100644
index f6f7d42..0000000
--- a/config/astal/components/bar/ui/modules/QuickView.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import { bind } from "astal";
-import AstalBattery from "gi://AstalBattery";
-import AstalBluetooth from "gi://AstalBluetooth";
-import AstalNetwork from "gi://AstalNetwork"
-import AstalWp from "gi://AstalWp";
-import Brightness from "../../util/brightness";
-import { Gtk } from "astal/gtk4";
-import QuickActions from "../QuickActions/QuickActions";
-
-const STATE = AstalNetwork.DeviceState;
-
-
-const QuickView = () => {
- const quickActions = QuickActions.QuickActions();
-
- return quickActions.popup()} child={
-
-
-
-
-
-
-
- { quickActions }
-
- }>
-}
-
-
-const NetworkWidget = () => {
- const network = AstalNetwork.get_default();
-
- return
- {
- if ( state === AstalNetwork.State.CONNECTING ) {
- return 'chronometer-reset-symbolic';
- } else if ( state === AstalNetwork.State.CONNECTED_LOCAL || state === AstalNetwork.State.CONNECTED_SITE || state === AstalNetwork.State.CONNECTED_GLOBAL ) {
- return 'network-wired-activated-symbolic';
- } else {
- return 'paint-unknown-symbolic';
- }
- } )} cssClasses={[ 'network-widget', 'quick-view-symbol' ]} visible={bind( network.wifi, 'state' ).as( state => state !== STATE.ACTIVATED )}>
- {
- if ( state === STATE.ACTIVATED ) {
- return network.wifi.iconName
- } else {
- return '';
- }
- } )} cssClasses={[ 'network-widget', 'quick-view-symbol' ]} visible={bind( network.wifi, 'state' ).as( state => state === STATE.ACTIVATED )}>
-
-
-
-}
-
-const BluetoothWidget = () => {
- const bluetooth = AstalBluetooth.get_default();
- const enabled = bind( bluetooth.adapter, "powered" );
- const connected = bind( bluetooth, "isConnected" );
-
- // For each connected BT device, render status
- return
- e )}>
- c )}>
- !c )}>
-
- !e )}>
-
- {bind( bluetooth, 'devices' ).as( devices => {
- return devices.map( device => {
- return c )}>
- icon )}>
-
-
- } );
- } )}
-
-
-}
-
-
-const BatteryWidget = () => {
- const battery = AstalBattery.get_default();
- if ( battery.get_is_present() ) {
- return icon )} cssClasses={[ 'quick-view-symbol' ]}>
- } else {
- return
- }
- // Else, no battery available -> Don't show the widget
-}
-
-
-const BrightnessWidget = () => {
- const brightness = Brightness.get_default();
- const screen_brightness = bind( brightness, "screen" );
-
- return
-}
-
-
-const Audio = () => {
- const wireplumber = AstalWp.get_default();
- if ( wireplumber ) {
- return
- icon )} cssClasses={[ 'quick-view-symbol' ]}>
- icon )} cssClasses={[ 'quick-view-symbol' ]}>
-
- } else {
- print( '[ WirePlumber ] Could not connect, Audio support in bar will be missing' );
- return ;
- }
-}
-
-// cssClasses={[ 'quick-view-symbol' ]}
-
-export default {
- QuickView
-}
diff --git a/config/astal/components/bar/ui/modules/SystemInfo.tsx b/config/astal/components/bar/ui/modules/SystemInfo.tsx
deleted file mode 100644
index d909b2c..0000000
--- a/config/astal/components/bar/ui/modules/SystemInfo.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import { exec, GLib, interval, Variable } from "astal"
-import { Gtk } from "astal/gtk4";
-import AstalBattery from "gi://AstalBattery?version=0.1";
-
-
-const FETCH_INTERVAL = 2000;
-
-
-const cpuUtil = Variable( '0%' );
-const ramUtil = Variable( '0%' );
-const ramUsed = Variable( '0MiB' );
-const gpuUtil = Variable( '0%' );
-let gpuName = 'card1';
-let enabled = false;
-
-const refreshStats = (): Stats => {
- gpuName = exec( `/bin/bash -c "ls /sys/class/drm/ | grep '^card[0-9]*$'"` );
- const cpuNameInSensors = 'CPUTIN'
- const stats = {
- kernel: exec( 'uname -sr' ),
- netSpeed: exec( `/bin/bash -c "interface=$(ip route get 8.8.8.8 | awk '{print $5; exit}') && cat \"/sys/class/net/$interface/speed\""` ),
- cpuTemp: exec( `/bin/bash -c "sensors | grep -m1 ${cpuNameInSensors} | awk '{print $2}'"` ),
- cpuClk: exec( `awk '/cpu MHz/ {sum+=$4; ++n} END {print sum/n " MHz"}' /proc/cpuinfo` ),
- gpuTemp: exec( `/bin/bash -c "sensors | grep -E 'edge' | awk '{print $2}'"` ),
- gpuClk: exec( `/bin/bash -c "cat /sys/class/drm/${gpuName}/device/pp_dpm_sclk | grep '\\*' | awk '{print $2 $3}'"` ),
- vram: Math.round( parseInt( exec( `cat /sys/class/drm/${gpuName}/device/mem_info_vram_used` ) ) / 1024 / 1024 ) + 'MiB',
- availableVRAM: Math.round( parseInt( exec( `cat /sys/class/drm/${gpuName}/device/mem_info_vram_total` ) ) / 1024 / 1024 ) + 'MiB',
- }
-
- return stats;
-}
-
-const systemStats: Variable = Variable( refreshStats() );
-
-
-const availableFeatures = {
- cpu: true,
- ram: true,
-}
-
-
-const featureTest = () => {
- // Check if awk & sed are available
- try {
- exec( 'awk -V' );
- exec( 'sed --version' );
- enabled = true;
- } catch ( e ) {
- printerr( '[ SysInfo ] AWK or SED missing! No system info will be available' );
- enabled = false;
- return;
- }
-
- // Check if mpstat is available
- try {
- exec( 'mpstat -V' );
- } catch ( e ) {
- availableFeatures.cpu = false;
- printerr( '[ SysInfo ] Feature Test for CPU info failed. mpstat from the sysstat package missing!' );
- }
-}
-
-const info = () => {
- return
-
-
- ;
-}
-
-
-const SystemInformationPanel = () => {
- const popover = new Gtk.Popover();
-
- popover.set_child( info() );
-
- return popover;
-}
-
-
-const sysInfoFetcher = () => {
- if ( enabled ) {
- if ( availableFeatures.cpu ) {
- cpuUtil.set( '' + Math.round( parseFloat( exec( `/bin/fish -c cpu-utilization` ) ) ) );
- }
- if ( availableFeatures.ram ) {
- ramUtil.set( '' + Math.round( parseFloat( exec( `/bin/bash -c "free | awk '/Mem/ { printf(\\"%.2f\\\\n\\", ($3/$2)*100) }'"` ) ) ) );
- ramUsed.set( exec( `/bin/bash -c \"free -h | awk '/^Mem:/ {print $3 \\" used of \\" $2}'\"` ).replaceAll( 'Gi', 'GiB' ).replaceAll( 'Mi', 'MiB' ) );
- }
- gpuUtil.set( exec( 'cat /sys/class/drm/card1/device/gpu_busy_percent' ) );
- }
-}
-
-
-const panel = SystemInformationPanel();
-
-
-const SystemInfo = () => {
- featureTest();
-
- const openSysInfo = async () => {
- panel.popup();
- systemStats.set( refreshStats() );
- }
-
- if ( enabled ) {
- sysInfoFetcher();
- interval( FETCH_INTERVAL, sysInfoFetcher );
-
- return openSysInfo() } child={
- v ) }>
-
-
-
-
-
-
- { panel }
-
- }>
- } else {
- return
- }
-}
-
-
-export default {
- SystemInfo,
- panel
-}
diff --git a/config/astal/components/bar/util/sysinfo.ts b/config/astal/components/bar/util/sysinfo.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/config/astal/no-avatar-icon.jpg b/config/astal/no-avatar-icon.jpg
new file mode 100644
index 0000000..87ecabf
Binary files /dev/null and b/config/astal/no-avatar-icon.jpg differ
diff --git a/config/astal/style.scss b/config/astal/style.scss
index f474652..3eebbed 100644
--- a/config/astal/style.scss
+++ b/config/astal/style.scss
@@ -1,6 +1,7 @@
-@use './components/notifications/notifications.scss';
-@use './components/bar/ui/bar.scss';
-@use './components/bar/ui/QuickActions/quickactions.scss';
+/* @use './components/notifications/notifications.scss'; */
+@use "./components/bar/bar.scss";
+@use "./components/QuickActions/quickactions.scss";
+@use "./util/colours.scss" as *;
* {
font-size: 1rem;
@@ -10,3 +11,13 @@ empty {
min-width: 0;
background-color: transparent;
}
+
+.title {
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+.title-2 {
+ font-size: 1.2rem;
+ font-weight: bold;
+}
diff --git a/config/astal/components/bar/util/brightness.ts b/config/astal/util/brightness.ts
similarity index 100%
rename from config/astal/components/bar/util/brightness.ts
rename to config/astal/util/brightness.ts
diff --git a/config/astal/util/colours.scss b/config/astal/util/colours.scss
new file mode 100644
index 0000000..e4a7257
--- /dev/null
+++ b/config/astal/util/colours.scss
@@ -0,0 +1,4 @@
+$fg-color: #{"@theme_fg_color"};
+$bg-color: #{"@theme_bg_color"};
+$accent-color: #202050;
+$accent-color-2: #202080;
diff --git a/config/astal/util/state.ts b/config/astal/util/state.ts
new file mode 100644
index 0000000..c1472c9
--- /dev/null
+++ b/config/astal/util/state.ts
@@ -0,0 +1,3 @@
+import { Variable } from "astal";
+
+export const quickActionsState = Variable( false );
diff --git a/scripts/get-profile-picture b/scripts/get-profile-picture
new file mode 100755
index 0000000..4240b2c
--- /dev/null
+++ b/scripts/get-profile-picture
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+USER_NAME=$(whoami)
+
+# Common paths to check
+paths=(
+ "/var/lib/AccountsService/icons/$USER_NAME"
+ "$HOME/.face"
+ "$HOME/.face.icon"
+)
+
+for path in "${paths[@]}"; do
+ if [ -f "$path" ]; then
+ echo "$path"
+ exit 0
+ fi
+done
+
+# Nothing found — exit silently or optionally with an error code
+exit 0