Added workspaces, window titles, volume, bluetooth, systray and date/time modules to the panel

This commit is contained in:
Jas Singh
2024-06-09 01:25:57 -07:00
commit 6ff50006f2
33 changed files with 2001 additions and 0 deletions

61
modules/bar/bar.js Normal file
View File

@@ -0,0 +1,61 @@
import { Workspaces } from "./workspaces/index.js";
import { ClientTitle } from "./window_title/index.js";
import { Media } from "./media/index.js";
import { Notification } from "./notification/index.js";
import { Volume } from "./volume/index.js";
import { Bluetooth } from "./bluetooth/index.js";
import { BatteryLabel } from "./battery/index.js";
import { Clock } from "./clock/index.js";
import { SysTray } from "./systray/index.js";
import { BarItemBox } from "../shared/barItemBox.js";
// layout of the bar
const Left = () => {
return Widget.Box({
class_name: "box-left",
hpack: "start",
spacing: 5,
children: [BarItemBox(Workspaces()), BarItemBox(ClientTitle())],
});
};
const Center = () => {
return Widget.Box({
class_name: "box-center",
spacing: 5,
children: [BarItemBox(Media()), BarItemBox(Notification())],
});
};
const Right = () => {
return Widget.Box({
class_name: "box-right",
hpack: "end",
spacing: 5,
children: [
BarItemBox(Volume()),
BarItemBox(Bluetooth()),
BarItemBox(BatteryLabel()),
BarItemBox(SysTray()),
BarItemBox(Clock()),
],
});
};
const Bar = (monitor = 0) => {
return Widget.Window({
name: `bar-${monitor}`,
class_name: "bar",
monitor,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
child: Widget.CenterBox({
start_widget: Left(),
center_widget: Center(),
end_widget: Right(),
}),
});
};
export { Bar };

View File

@@ -0,0 +1,32 @@
const battery = await Service.import("battery");
const BatteryLabel = () => {
const isVis = Variable(battery.available);
const value = battery.bind("percent").as((p) => (p > 0 ? p / 100 : 0));
const icon = battery
.bind("percent")
.as((p) => `battery-level-${Math.floor(p / 10) * 10}-symbolic`);
battery.connect("changed", ({ available }) => {
isVis.value = available;
});
return {
component: Widget.Box({
class_name: "battery",
visible: battery.bind("available"),
children: [
Widget.Icon({ icon }),
Widget.LevelBar({
widthRequest: 20,
vpack: "center",
value,
}),
],
}),
isVis,
};
};
export { BatteryLabel };

View File

@@ -0,0 +1,24 @@
const bluetooth = await Service.import('bluetooth')
const Bluetooth = () => {
const btIcon = Widget.Label({
label: bluetooth.bind("enabled").as((v) => v ? "󰂯 " : "󰂲 "),
class_name: "bar-bt_icon",
});
const btText = Widget.Label({
label: bluetooth.bind("enabled").as((v) => v ? "On" : "Off"),
class_name: "bar-bt_label",
});
return {
component: Widget.Box({
class_name: "volume",
children: [btIcon, btText],
}),
isVisible: true,
};
}
export { Bluetooth }

View File

@@ -0,0 +1,15 @@
const date = Variable("", {
poll: [1000, 'date "+󰃭 %a %b%e %I:%M:%S %p"'],
});
const Clock = () => {
return {
component: Widget.Label({
class_name: "clock",
label: date.bind(),
}),
isVisible: true,
};
};
export { Clock };

View File

@@ -0,0 +1,25 @@
const mpris = await Service.import("mpris");
const Media = () => {
const label = Utils.watch("", mpris, "player-changed", () => {
if (mpris.players[0]) {
const { track_artists, track_title } = mpris.players[0];
return `${track_artists.join(", ")} - ${track_title}`;
} else {
return "Nothing is playing";
}
});
return {
component: Widget.Button({
class_name: "media",
on_primary_click: () => mpris.getPlayer("")?.playPause(),
on_scroll_up: () => mpris.getPlayer("")?.next(),
on_scroll_down: () => mpris.getPlayer("")?.previous(),
child: Widget.Label({ label }),
}),
isVisible: false,
};
};
export { Media };

View File

@@ -0,0 +1,24 @@
const notifications = await Service.import("notifications");
// we don't need dunst or any other notification daemon
// because the Notifications module is a notification daemon itself
const Notification = () => {
const popups = notifications.bind("popups");
return {
component: Widget.Box({
class_name: "notification",
visible: popups.as((p) => p.length > 0),
children: [
Widget.Icon({
icon: "preferences-system-notifications-symbolic",
}),
Widget.Label({
label: popups.as((p) => p[0]?.summary || ""),
}),
],
}),
isVisible: false,
};
};
export { Notification };

View File

@@ -0,0 +1,36 @@
const systemtray = await Service.import("systemtray");
const SysTray = () => {
const isVis = Variable(false);
const items = systemtray.bind("items").as((items) => {
isVis.value = items.length > 0;
return items.map((item) => {
if (item.menu !== undefined) {
item.menu["class_name"] = "systray-menu";
}
return Widget.Button({
cursor: "pointer",
child: Widget.Icon({
icon: item.bind("icon"),
size: 18,
}),
on_primary_click: (_, event) => item.activate(event),
on_secondary_click: (_, event) => item.openMenu(event),
tooltip_markup: item.bind("tooltip_markup"),
});
});
});
return {
component: Widget.Box({
class_name: "systray",
children: items,
}),
isVisible: true,
isVis,
};
};
export { SysTray };

View File

@@ -0,0 +1,41 @@
const audio = await Service.import("audio");
const Volume = () => {
const icons = {
101: "󰕾",
66: "󰕾",
34: "󰖀",
1: "󰕿",
0: "󰝟",
};
const getIcon = () => {
const icon = audio.speaker.is_muted
? 0
: [101, 66, 34, 1, 0].find(
(threshold) => threshold <= audio.speaker.volume * 100,
);
return icons[icon];
};
const volIcn = Widget.Label({
label: audio.speaker.bind("volume").as(() => getIcon()),
class_name: "bar-volume_icon",
});
const volPct = Widget.Label({
label: audio.speaker.bind("volume").as((v) => ` ${Math.floor(v * 100)}%`),
class_name: "bar-volume_percentage",
});
return {
component: Widget.Box({
class_name: "volume",
children: [volIcn, volPct],
}),
isVisible: true,
};
};
export { Volume };

View File

@@ -0,0 +1,39 @@
const hyprland = await Service.import("hyprland");
const filterTitle = (titleString) => {
const windowTitleMap = [
["(.*) - NVIM", " NeoVim"],
["(.*) - nvim", " NeoVim"],
["(.*) - VIM", " NeoVim"],
["(.*)vim (.*)", " NeoVim"],
["(.*) — Mozilla Firefox", "󰈹 Firefox"],
["(.*) - Microsoft(.*)Edge", "󰇩 Edge"],
["(.*) - Discord", " Discord"],
["(.*) — Dolphin", " Dolphin"],
["Plex", "󰚺 Plex"],
["(.*) Steam", " Steam"],
[" ", "󰇄 Desktop"],
["(.*) Spotify Free", "󰓇 Spotify"],
["(.*)Spotify Premium", "󰓇 Spotify"],
[" ~", " Terminal"],
["(.*) - Obsidian(.*)", "󱓧 Obsidian"],
];
const foundMatch = windowTitleMap.find((wt) =>
RegExp(wt[0]).test(titleString),
);
return foundMatch ? foundMatch[1] : titleString;
};
const ClientTitle = () => {
return {
component: Widget.Label({
class_name: "window_title",
label: hyprland.active.client.bind("title").as((v) => filterTitle(v)),
}),
isVisible: true,
};
};
export { ClientTitle };

View File

@@ -0,0 +1,44 @@
const hyprland = await Service.import("hyprland");
function range(length, start = 1) {
return Array.from({ length }, (_, i) => i + start);
}
const Workspaces = (ws) => {
return {
component: Widget.Box({
class_name: "workspaces",
children: range(ws || 8).map((i) =>
Widget.Label({
attribute: i,
vpack: "center",
label: `${i}`,
setup: (self) =>
self.hook(hyprland, () => {
self.toggleClassName(
"active",
hyprland.active.workspace.id === i,
);
self.toggleClassName(
"occupied",
(hyprland.getWorkspace(i)?.windows || 0) > 0,
);
}),
}),
),
setup: (box) => {
if (ws === 0) {
box.hook(hyprland.active.workspace, () =>
box.children.map((btn) => {
btn.visible = hyprland.workspaces.some(
(ws) => ws.id === btn.attribute,
);
}),
);
}
},
}),
isVisible: true,
};
};
export { Workspaces };