aboutsummaryrefslogtreecommitdiff
path: root/home-manager/programs/ags
diff options
context:
space:
mode:
Diffstat (limited to 'home-manager/programs/ags')
-rw-r--r--home-manager/programs/ags/bar/config.js203
-rw-r--r--home-manager/programs/ags/bar/style.css26
-rw-r--r--home-manager/programs/ags/default.nix8
3 files changed, 237 insertions, 0 deletions
diff --git a/home-manager/programs/ags/bar/config.js b/home-manager/programs/ags/bar/config.js
new file mode 100644
index 0000000..8abf671
--- /dev/null
+++ b/home-manager/programs/ags/bar/config.js
@@ -0,0 +1,203 @@
+// importing
+import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
+import Notifications from 'resource:///com/github/Aylur/ags/service/notifications.js';
+import Mpris from 'resource:///com/github/Aylur/ags/service/mpris.js';
+import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
+import Battery from 'resource:///com/github/Aylur/ags/service/battery.js';
+import SystemTray from 'resource:///com/github/Aylur/ags/service/systemtray.js';
+import App from 'resource:///com/github/Aylur/ags/app.js';
+import Widget from 'resource:///com/github/Aylur/ags/widget.js';
+import { exec, execAsync } from 'resource:///com/github/Aylur/ags/utils.js';
+
+// widgets can be only assigned as a child in one container
+// so to make a reuseable widget, just make it a function
+// then you can use it by calling simply calling it
+
+const Workspaces = () => Widget.Box({
+ className: 'workspaces',
+ connections: [[Hyprland.active.workspace, self => {
+ // generate an array [1..10] then make buttons from the index
+ const arr = Array.from({ length: 10 }, (_, i) => i + 1);
+ self.children = arr.map(i => Widget.Button({
+ onClicked: () => execAsync(`hyprctl dispatch workspace ${i}`),
+ child: Widget.Label(`${i}`),
+ className: Hyprland.active.workspace.id == i ? 'focused' : '',
+ }));
+ }]],
+});
+
+const ClientTitle = () => Widget.Label({
+ className: 'client-title',
+ binds: [
+ ['label', Hyprland.active.client, 'title'],
+ ],
+});
+
+const Clock = () => Widget.Label({
+ className: 'clock',
+ connections: [
+ // this is bad practice, since exec() will block the main event loop
+ // in the case of a simple date its not really a problem
+ [1000, self => self.label = exec('date "+%H:%M:%S %b %e."')],
+
+ // this is what you should do
+ [1000, self => execAsync(['date', '+%H:%M:%S %b %e.'])
+ .then(date => self.label = date).catch(console.error)],
+ ],
+});
+
+// we don't need dunst or any other notification daemon
+// because the Notifications module is a notification daemon itself
+const Notification = () => Widget.Box({
+ className: 'notification',
+ children: [
+ Widget.Icon({
+ icon: 'preferences-system-notifications-symbolic',
+ connections: [
+ [Notifications, self => self.visible = Notifications.popups.length > 0],
+ ],
+ }),
+ Widget.Label({
+ connections: [[Notifications, self => {
+ self.label = Notifications.popups[0]?.summary || '';
+ }]],
+ }),
+ ],
+});
+
+const Media = () => Widget.Button({
+ className: 'media',
+ onPrimaryClick: () => Mpris.getPlayer('')?.playPause(),
+ onScrollUp: () => Mpris.getPlayer('')?.next(),
+ onScrollDown: () => Mpris.getPlayer('')?.previous(),
+ child: Widget.Label({
+ connections: [[Mpris, self => {
+ const mpris = Mpris.getPlayer('');
+ // mpris player can be undefined
+ if (mpris)
+ self.label = `${mpris.trackArtists.join(', ')} - ${mpris.trackTitle}`;
+ else
+ self.label = 'Nothing is playing';
+ }]],
+ }),
+});
+
+const Volume = () => Widget.Box({
+ className: 'volume',
+ css: 'min-width: 180px',
+ children: [
+ Widget.Stack({
+ items: [
+ // tuples of [string, Widget]
+ ['101', Widget.Icon('audio-volume-overamplified-symbolic')],
+ ['67', Widget.Icon('audio-volume-high-symbolic')],
+ ['34', Widget.Icon('audio-volume-medium-symbolic')],
+ ['1', Widget.Icon('audio-volume-low-symbolic')],
+ ['0', Widget.Icon('audio-volume-muted-symbolic')],
+ ],
+ connections: [[Audio, self => {
+ if (!Audio.speaker)
+ return;
+
+ if (Audio.speaker.isMuted) {
+ self.shown = '0';
+ return;
+ }
+
+ const show = [101, 67, 34, 1, 0].find(
+ threshold => threshold <= Audio.speaker.volume * 100);
+
+ self.shown = `${show}`;
+ }, 'speaker-changed']],
+ }),
+ Widget.Slider({
+ hexpand: true,
+ drawValue: false,
+ onChange: ({ value }) => Audio.speaker.volume = value,
+ connections: [[Audio, self => {
+ self.value = Audio.speaker?.volume || 0;
+ }, 'speaker-changed']],
+ }),
+ ],
+});
+
+const BatteryLabel = () => Widget.Box({
+ className: 'battery',
+ children: [
+ Widget.Icon({
+ connections: [[Battery, self => {
+ self.icon = `battery-level-${Math.floor(Battery.percent / 10) * 10}-symbolic`;
+ }]],
+ }),
+ Widget.ProgressBar({
+ vpack: 'center',
+ connections: [[Battery, self => {
+ if (Battery.percent < 0)
+ return;
+
+ self.fraction = Battery.percent / 100;
+ }]],
+ }),
+ ],
+});
+
+const SysTray = () => Widget.Box({
+ connections: [[SystemTray, self => {
+ self.children = SystemTray.items.map(item => Widget.Button({
+ child: Widget.Icon({ binds: [['icon', item, 'icon']] }),
+ onPrimaryClick: (_, event) => item.activate(event),
+ onSecondaryClick: (_, event) => item.openMenu(event),
+ binds: [['tooltip-markup', item, 'tooltip-markup']],
+ }));
+ }]],
+});
+
+// layout of the bar
+const Left = () => Widget.Box({
+ children: [
+ Workspaces(),
+ ClientTitle(),
+ ],
+});
+
+const Center = () => Widget.Box({
+ children: [
+ Media(),
+ Notification(),
+ ],
+});
+
+const Right = () => Widget.Box({
+ hpack: 'end',
+ children: [
+ Volume(),
+ BatteryLabel(),
+ Clock(),
+ SysTray(),
+ ],
+});
+
+const Bar = ({ monitor } = {}) => Widget.Window({
+ name: `bar-${monitor}`, // name has to be unique
+ className: 'bar',
+ monitor,
+ anchor: ['top', 'left', 'right'],
+ exclusive: true,
+ child: Widget.CenterBox({
+ startWidget: Left(),
+ centerWidget: Center(),
+ endWidget: Right(),
+ }),
+})
+
+// exporting the config so ags can manage the windows
+export default {
+ style: App.configDir + '/style.css',
+ windows: [
+ Bar(),
+
+ // you can call it, for each monitor
+ // Bar({ monitor: 0 }),
+ // Bar({ monitor: 1 })
+ ],
+};
diff --git a/home-manager/programs/ags/bar/style.css b/home-manager/programs/ags/bar/style.css
new file mode 100644
index 0000000..900d84f
--- /dev/null
+++ b/home-manager/programs/ags/bar/style.css
@@ -0,0 +1,26 @@
+* {
+ font-family: Material Design Icons, Rubik Medium;
+ font-size: 14px;
+ color: #6e6a86;
+}
+
+.window {
+ border-radius: 10px;
+ background: #1f1d2e;
+ border: 3px solid #363a4f;
+}
+
+.battery {
+ margin-right: 6px;
+}
+
+.workspaces button {
+ font-size: 15px;
+ transition: all 100ms ease-out;
+}
+
+.workspaces button:active {
+ font-weight: bolder;
+ color: #908caa;
+ transition: all 100ms ease-out;
+}
diff --git a/home-manager/programs/ags/default.nix b/home-manager/programs/ags/default.nix
new file mode 100644
index 0000000..dbb8228
--- /dev/null
+++ b/home-manager/programs/ags/default.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+{
+ programs.ags = {
+ enable = true;
+ configDir = ./bar;
+ };
+}