import type { Monitor } from "@/services/monitors";
import Players from "@/services/players";
import Updates from "@/services/updates";
import { getAppCategoryIcon } from "@/utils/icons";
import { ellipsize } from "@/utils/strings";
import { bindCurrentTime, osIcon } from "@/utils/system";
import type { AstalWidget } from "@/utils/types";
import { setupCustomTooltip } from "@/utils/widgets";
import type PopupWindow from "@/widgets/popupwindow";
import { execAsync, register, Variable } from "astal";
import { bind, kebabify } from "astal/binding";
import { App, Astal, astalify, Gdk, Gtk, type ConstructProps } from "astal/gtk3";
import { bar as config } from "config";
import AstalBattery from "gi://AstalBattery";
import AstalBluetooth from "gi://AstalBluetooth";
import AstalHyprland from "gi://AstalHyprland";
import AstalNetwork from "gi://AstalNetwork";
import AstalNotifd from "gi://AstalNotifd";
import AstalTray from "gi://AstalTray";
import AstalWp01 from "gi://AstalWp";
const hyprland = AstalHyprland.get_default();
const getBatteryIcon = (perc: number) => {
if (perc < 0.1) return "";
if (perc < 0.2) return "";
if (perc < 0.3) return "";
if (perc < 0.4) return "";
if (perc < 0.5) return "";
if (perc < 0.6) return "";
if (perc < 0.7) return "";
if (perc < 0.8) return "";
if (perc < 0.9) return "";
return "";
};
const formatSeconds = (sec: number) => {
if (sec >= 3600) return `${Math.floor(sec / 3600)} hour${sec % 3600 === 0 ? "" : "s"}`;
if (sec >= 60) return `${Math.floor(sec / 60)} minute${sec % 60 === 0 ? "" : "s"}`;
return `${sec} second${sec === 1 ? "" : "s"}`;
};
const hookFocusedClientProp = (
self: AstalWidget,
prop: keyof AstalHyprland.Client,
callback: (c: AstalHyprland.Client | null) => void
) => {
let id: number | null = null;
let lastClient: AstalHyprland.Client | null = null;
self.hook(hyprland, "notify::focused-client", () => {
if (id) lastClient?.disconnect(id);
lastClient = hyprland.focusedClient; // Can be null
id = lastClient?.connect(`notify::${kebabify(prop)}`, () => callback(lastClient));
callback(lastClient);
});
self.connect("destroy", () => id && lastClient?.disconnect(id));
callback(lastClient);
};
const togglePopup = (self: JSX.Element, event: Astal.ClickEvent, name: string) => {
const popup = App.get_window(name) as PopupWindow | null;
if (popup) {
if (popup.visible) popup.hide();
else popup.popup_at_widget(self, event);
}
};
const OSIcon = () => (
);
const ActiveWindow = () => (
{
const title = Variable("");
const updateTooltip = (c: AstalHyprland.Client | null) =>
title.set(c?.class && c?.title ? `${c.class}: ${c.title}` : "");
hookFocusedClientProp(self, "class", updateTooltip);
hookFocusedClientProp(self, "title", updateTooltip);
updateTooltip(hyprland.focusedClient);
const window = setupCustomTooltip(self, bind(title));
if (window) {
self.hook(title, (_, v) => !v && window.hide());
self.hook(window, "map", () => !title.get() && window.hide());
}
}}
>
);
const MediaPlaying = () => {
const players = Players.get_default();
const getLabel = (fallback = "") =>
players.lastPlayer ? `${players.lastPlayer.title} - ${players.lastPlayer.artist}` : fallback;
return (
);
};
const Workspace = ({ idx }: { idx: number }) => {
let wsId = hyprland.focusedWorkspace
? Math.floor((hyprland.focusedWorkspace.id - 1) / config.wsPerGroup) * config.wsPerGroup + idx
: idx;
return (