import type { Monitor } from "@/services/monitors"; import Players from "@/services/players"; import Updates from "@/services/updates"; import { getAppCategoryIcon } from "@/utils/icons"; import { bindCurrentTime, osIcon } from "@/utils/system"; import type { AstalWidget } from "@/utils/types"; import { setupCustomTooltip } from "@/utils/widgets"; import ScreenCorner from "@/widgets/screencorner"; import { execAsync, GLib, Variable } from "astal"; import { bind, kebabify } from "astal/binding"; import { App, Astal, Gtk, Widget } 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 AstalWp from "gi://AstalWp"; import { switchPane } from "./sidebar"; interface ClassNameProps { beforeSpacer: boolean; afterSpacer: boolean; first: boolean; last: boolean; } interface ModuleProps extends ClassNameProps { monitor: Monitor; } 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) { const hours = Math.floor(sec / 3600); let str = `${hours} hour${hours === 1 ? "" : "s"}`; const mins = Math.floor((sec % 3600) / 60); if (mins > 0) str += ` ${mins} minute${mins === 1 ? "" : "s"}`; return str; } else if (sec >= 60) { const mins = Math.floor(sec / 60); return `${mins} minute${mins === 1 ? "" : "s"}`; } else 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 getClassName = ({ beforeSpacer, afterSpacer, first, last }: ClassNameProps) => `${beforeSpacer ? "before-spacer" : ""} ${afterSpacer ? "after-spacer" : ""}` + ` ${first ? "first" : ""} ${last ? "last" : ""}`; const getModule = (module: string) => { module = module.toLowerCase(); if (module === "osicon") return OSIcon; if (module === "activewindow") return ActiveWindow; if (module === "mediaplaying") return MediaPlaying; if (module === "workspaces") return Workspaces; if (module === "tray") return Tray; if (module === "statusicons") return StatusIcons; if (module === "pkgupdates") return PkgUpdates; if (module === "notifcount") return NotifCount; if (module === "battery") return Battery; if (module === "datetime") return DateTime; if (module === "power") return Power; if (module === "brightnessspacer") return BrightnessSpacer; if (module === "volumespacer") return VolumeSpacer; return () => null; }; const isSpacer = (module?: string) => module?.toLowerCase().endsWith("spacer") ?? false; const OSIcon = ({ monitor, ...props }: ModuleProps) => ( ); const ActiveWindow = ({ monitor, ...props }: ModuleProps) => ( { 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 = ({ monitor, ...props }: ModuleProps) => { const players = Players.get_default(); const getLabel = (fallback = "") => players.lastPlayer ? `${players.lastPlayer.title} - ${players.lastPlayer.artist}` : fallback; return ( ); }; const Workspace = ({ idx }: { idx: number }) => { const wsId = Variable.derive([bind(hyprland, "focusedWorkspace"), config.modules.workspaces.shown], (f, s) => f ? Math.floor((f.id - 1) / s) * s + idx : idx ); const label = (