// ── violet-chaton v2 — Bar widget ─────────────────────────────────────────── // 3 pills glassmorphism — modulaire selon compositor const audio = await Service.import("audio"); const battery = await Service.import("battery"); const network = await Service.import("network"); const systemtray = await Service.import("systemtray"); const mpris = await Service.import("mpris"); // ── Helpers ───────────────────────────────────────────────────────────────── const Separator = () => Widget.Label({ className: "separator", label: "│" }); const Clock = () => Widget.Label({ className: "clock", connections: [[1000, (self) => { self.label = Utils.exec("date +%H:%M"); }]], }); const DateWidget = () => Widget.Label({ className: "date", connections: [[60000, (self) => { self.label = Utils.exec("date '+%a %d %b'"); }]], }); // ── System ────────────────────────────────────────────────────────────────── const CPU = () => Widget.Label({ className: "cpu", connections: [[2000, (self) => { const usage = Math.round( Number(Utils.exec(`bash -c "top -bn1 | awk '/^%Cpu/ {print 100-$8}'"`) )); self.label = `󰻠 ${usage}%`; self.toggleClassName("warning", usage > 70); self.toggleClassName("critical", usage > 90); }]], }); const RAM = () => Widget.Label({ className: "ram", connections: [[2000, (self) => { const used = Utils.exec(`bash -c "free -m | awk '/^Mem:/ {printf \"%.1f\", $3/1024}'"`) const total = Utils.exec(`bash -c "free -m | awk '/^Mem:/ {printf \"%.1f\", $2/1024}'"`) const pct = Math.round((parseFloat(used) / parseFloat(total)) * 100); self.label = `󰑭 ${used}G`; self.toggleClassName("warning", pct > 70); self.toggleClassName("critical", pct > 90); }]], }); // ── Network ───────────────────────────────────────────────────────────────── const Network = () => Widget.Label({ className: "network", connections: [[network, (self) => { if (network.primary === "wifi") { const wifi = network.wifi; self.label = `󰤨 ${wifi?.ssid || ""}`; self.toggleClassName("wifi", true); self.toggleClassName("disconnected", false); } else if (network.primary === "wired") { self.label = "󰈀 Eth"; self.toggleClassName("wifi", false); self.toggleClassName("disconnected", false); } else { self.label = "󰤮 "; self.toggleClassName("disconnected", true); } }]], }); // ── Volume ────────────────────────────────────────────────────────────────── const Volume = () => Widget.Button({ className: "volume", onClicked: () => { audio.speaker.isMuted = !audio.speaker.isMuted; }, child: Widget.Label({ connections: [[audio, (self) => { const vol = Math.round((audio.speaker?.volume || 0) * 100); const muted = audio.speaker?.isMuted; const icon = muted ? "󰝟" : vol > 66 ? "󰕾" : vol > 33 ? "󰖀" : "󰕿"; self.label = `${icon} ${vol}%`; self.parent?.toggleClassName("muted", muted); }, "speaker-changed"]], }), }); // ── Battery ───────────────────────────────────────────────────────────────── const Battery = () => Widget.Label({ className: "battery", visible: battery.bind("available"), connections: [[battery, (self) => { const pct = battery.percent; const charging = battery.charging; const icon = charging ? "󰂄" : pct > 80 ? "󰁹" : pct > 60 ? "󰂀" : pct > 40 ? "󰁾" : pct > 20 ? "󰁻" : "󰂎"; self.label = `${icon} ${pct}%`; self.toggleClassName("charging", charging); self.toggleClassName("low", pct <= 20 && !charging); self.toggleClassName("warning", pct <= 30 && !charging); }]], }); // ── Media ─────────────────────────────────────────────────────────────────── const Media = () => Widget.Label({ className: "media", connections: [[mpris, (self) => { const player = mpris.players[0]; if (!player) { self.visible = false; return; } self.visible = true; const artist = player.trackArtists?.join(", ") || ""; const title = player.trackTitle || ""; const icon = player.playBackStatus === "Playing" ? " " : " "; self.label = `${icon}${artist ? artist + " — " : ""}${title}`.slice(0, 50); self.toggleClassName("paused", player.playBackStatus !== "Playing"); }]], }); // ── Systray ───────────────────────────────────────────────────────────────── const SysTray = () => Widget.Box({ className: "systray", children: systemtray.bind("items").transform((items) => items.map((item) => Widget.Button({ child: Widget.Icon({ icon: item.bind("icon"), size: 16 }), tooltipMarkup: item.bind("tooltip-markup"), onPrimaryClick: (_, event) => item.activate(event), onSecondaryClick: (_, event) => item.openMenu(event), }) ) ), }); // ── Launcher button ───────────────────────────────────────────────────────── const LauncherBtn = () => Widget.Button({ className: "launcher-btn", label: "󱄅", onClicked: () => App.toggleWindow("launcher"), }); // ── Power button ──────────────────────────────────────────────────────────── const PowerBtn = () => Widget.Button({ className: "power-btn", label: "⏻", onClicked: () => Utils.exec("bash -c 'systemctl poweroff'"), }); // ── Workspaces (Hyprland only) ────────────────────────────────────────────── const Workspaces = (compositor) => { if (compositor !== "hyprland") return Widget.Box({}); const hyprland = await Service.import("hyprland"); return Widget.Box({ className: "workspaces", children: hyprland.bind("workspaces").transform((ws) => ws.sort((a, b) => a.id - b.id) .filter((w) => w.id > 0) .map((w) => Widget.Button({ onClicked: () => hyprland.messageAsync(`dispatch workspace ${w.id}`), child: Widget.Label({ label: `${w.id}` }), className: hyprland.active.workspace.bind("id").transform( (id) => id === w.id ? "active" : w.windows > 0 ? "occupied" : "" ), }) ) ), }); }; // ── Bar assembly ──────────────────────────────────────────────────────────── export const Bar = (monitor, compositor) => Widget.Window({ name: `bar-${monitor}`, monitor, anchor: ["top", "left", "right"], exclusivity: "exclusive", className: "bar", child: Widget.CenterBox({ startWidget: Widget.Box({ className: "modules-left", children: [ LauncherBtn(), Separator(), Workspaces(compositor), Separator(), CPU(), RAM(), ], }), centerWidget: Widget.Box({ className: "modules-center", children: [ Media(), ], }), endWidget: Widget.Box({ className: "modules-right", hpack: "end", children: [ Network(), Separator(), Volume(), Separator(), Battery(), Separator(), DateWidget(), Clock(), Separator(), SysTray(), Separator(), PowerBtn(), ], }), }), });