feat: move status-bar to seperate repository, initial commit
This commit is contained in:
46
widget/Bar.tsx
Normal file
46
widget/Bar.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { App, Astal, Gtk, Gdk } from "astal/gtk3";
|
||||
import Workspaces from "./workspaces";
|
||||
import Audio from "./audio";
|
||||
import NetworkModule from "./network";
|
||||
import Cpu from "./cpu-widget";
|
||||
import Ram from "./ram";
|
||||
import Disk from "./disk";
|
||||
import Battery from "./battery";
|
||||
import Time from "./time";
|
||||
import Title from "./title";
|
||||
|
||||
export default function Bar(gdkmonitor: Gdk.Monitor) {
|
||||
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||
|
||||
//@ts-ignore
|
||||
return (
|
||||
<window
|
||||
className="Bar"
|
||||
gdkmonitor={gdkmonitor}
|
||||
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||
anchor={TOP | LEFT | RIGHT}
|
||||
application={App}
|
||||
>
|
||||
<centerbox>
|
||||
<box hexpand halign={Gtk.Align.START}>
|
||||
<box className="nix-icon">
|
||||
<icon icon="nixos-3" />
|
||||
</box>
|
||||
<Workspaces monitor={gdkmonitor} />
|
||||
</box>
|
||||
<box className="client-title">
|
||||
<Title />
|
||||
</box>
|
||||
<box hexpand halign={Gtk.Align.END}>
|
||||
<Audio />
|
||||
<NetworkModule />
|
||||
<Cpu />
|
||||
<Ram />
|
||||
<Disk />
|
||||
<Battery />
|
||||
<Time />
|
||||
</box>
|
||||
</centerbox>
|
||||
</window>
|
||||
);
|
||||
}
|
||||
17
widget/audio.tsx
Normal file
17
widget/audio.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { bind, Variable } from "astal"
|
||||
import AstalWp from "gi://AstalWp?version=0.1"
|
||||
|
||||
export default function Audio() {
|
||||
const speaker = AstalWp.get_default()?.default_speaker!
|
||||
const derived = Variable.derive([bind(speaker, "volume"), bind(speaker, "mute")], (volume: number, muted: boolean) => {
|
||||
if (muted) {
|
||||
return { label: ` (muted)`, muted }
|
||||
}
|
||||
return { label: `${Math.floor(volume * 100)}% ` }
|
||||
})
|
||||
|
||||
return <box className={derived((v) => ["status-box", v.muted && "inactive"].filter(Boolean).join(" "))}>
|
||||
<label
|
||||
label={derived((v) => v.label)} />
|
||||
</box>
|
||||
}
|
||||
21
widget/battery.tsx
Normal file
21
widget/battery.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { bind, Variable } from "astal"
|
||||
import AstalBattery from "gi://AstalBattery?version=0.1"
|
||||
|
||||
export default function Battery() {
|
||||
const battery = AstalBattery.get_default()
|
||||
const battery_info = Variable.derive([bind(battery, "percentage"), bind(battery, "charging")], (percentage, charging) => {
|
||||
const full_percentage = Math.floor(percentage * 100)
|
||||
if (charging) {
|
||||
return { label: `${full_percentage == 100 ? "FULL" : "CHR"}: ${full_percentage}%` }
|
||||
}
|
||||
return { label: `${full_percentage == 100 ? "FULL" : "BAT"}: ${full_percentage}%` }
|
||||
})
|
||||
|
||||
if (!battery.is_battery) {
|
||||
return <></>
|
||||
}
|
||||
return <box className="status-box">
|
||||
<label label={battery_info((i) => i.label)} />
|
||||
</box>
|
||||
}
|
||||
|
||||
19
widget/cpu-widget.tsx
Normal file
19
widget/cpu-widget.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { bind, GLib, Variable } from "astal";
|
||||
import { calc_cpu_usage, get_cpu_snapshot } from "./cpu";
|
||||
|
||||
let s1 = get_cpu_snapshot();
|
||||
let cpu_usage_percent = Variable(0);
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
|
||||
const s2 = get_cpu_snapshot();
|
||||
cpu_usage_percent.set(calc_cpu_usage(s1, s2));
|
||||
s1 = s2;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
export default function Cpu() {
|
||||
return <box className="status-box">
|
||||
<label label={bind(cpu_usage_percent).as((u) => `${u}% `)} />
|
||||
</box>
|
||||
}
|
||||
|
||||
58
widget/cpu.ts
Normal file
58
widget/cpu.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { GLib, Variable } from "astal";
|
||||
import GTop from "gi://GTop";
|
||||
import Wp05 from "gi://Wp";
|
||||
|
||||
type Snapshot = {
|
||||
total: number;
|
||||
user: number;
|
||||
sys: number;
|
||||
idle: number;
|
||||
};
|
||||
|
||||
export function get_cpu_snapshot() {
|
||||
const cpu = new GTop.glibtop_cpu();
|
||||
GTop.glibtop_get_cpu(cpu);
|
||||
return {
|
||||
total: cpu.total,
|
||||
user: cpu.user + cpu.nice,
|
||||
sys: cpu.sys,
|
||||
idle: cpu.idle,
|
||||
};
|
||||
}
|
||||
|
||||
export function calc_cpu_usage(a: Snapshot, b: Snapshot) {
|
||||
const total_diff = b.total - a.total;
|
||||
const active_diff = b.user + b.sys - (a.user + a.sys);
|
||||
return Math.round(total_diff > 0 ? (100 * active_diff) / total_diff : 0);
|
||||
}
|
||||
|
||||
export function get_ram_info() {
|
||||
const mem = new GTop.glibtop_mem();
|
||||
GTop.glibtop_get_mem(mem);
|
||||
return {
|
||||
total: mem.total,
|
||||
used: mem.total - mem.free - mem.cached - mem.buffer,
|
||||
free: mem.free,
|
||||
};
|
||||
}
|
||||
|
||||
function format_bytes(bytes: number) {
|
||||
let units = ["B", "KiB", "MiB", "GiB", "TiB"];
|
||||
let i = 0;
|
||||
let num: number = bytes;
|
||||
while (num >= 1024 && i < units.length - 1) {
|
||||
num /= 1024;
|
||||
i++;
|
||||
}
|
||||
|
||||
return `${num.toFixed(2)}${units[i]}`;
|
||||
}
|
||||
|
||||
export function get_disk_space() {
|
||||
const usage = new GTop.glibtop_fsusage();
|
||||
GTop.glibtop_get_fsusage(usage, "/");
|
||||
|
||||
const free_bytes = usage.bavail * usage.block_size;
|
||||
|
||||
return format_bytes(free_bytes);
|
||||
}
|
||||
10
widget/disk.tsx
Normal file
10
widget/disk.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { bind, Variable } from "astal"
|
||||
import { get_disk_space } from "./cpu"
|
||||
|
||||
let disk_space = Variable(get_disk_space()).poll(5000, () => get_disk_space())
|
||||
export default function Disk() {
|
||||
return <box className="status-box">
|
||||
<label label={bind(disk_space).as((s) => `${s}`)} />
|
||||
</box>
|
||||
}
|
||||
|
||||
47
widget/network.tsx
Normal file
47
widget/network.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { bind, Variable } from "astal";
|
||||
import AstalNetwork from "gi://AstalNetwork?version=0.1";
|
||||
|
||||
const disconnected_label = { label: `Disconnected ⚠` };
|
||||
|
||||
function create_label(
|
||||
primary: AstalNetwork.Primary,
|
||||
wired: AstalNetwork.Wired,
|
||||
wifi?: AstalNetwork.Wifi,
|
||||
) {
|
||||
if (primary === AstalNetwork.Primary.WIRED) {
|
||||
return { label: `🖧 Wired ${wired.device.interface}` };
|
||||
}
|
||||
if (!wifi) {
|
||||
return disconnected_label;
|
||||
}
|
||||
if (wifi.active_access_point !== null) {
|
||||
return { label: `${wifi.ssid} (${wifi.strength}%) ` };
|
||||
}
|
||||
return disconnected_label;
|
||||
}
|
||||
|
||||
export default function NetworkModule() {
|
||||
const network = AstalNetwork.get_default();
|
||||
const wifi = network.wifi;
|
||||
const wired = network.wired;
|
||||
|
||||
let derived;
|
||||
if (!wifi) {
|
||||
derived = Variable.derive([bind(network, "primary")], (primary) => {
|
||||
return create_label(primary, wired);
|
||||
});
|
||||
} else {
|
||||
derived = Variable.derive(
|
||||
[bind(network, "primary"), bind(wifi, "ssid"), bind(wifi, "strength")],
|
||||
(primary) => {
|
||||
return create_label(primary, wired, wifi);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<box className="status-box">
|
||||
<label label={derived((v) => v.label)} />
|
||||
</box>
|
||||
);
|
||||
}
|
||||
16
widget/ram.tsx
Normal file
16
widget/ram.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { bind, GLib, Variable } from "astal"
|
||||
import { get_ram_info } from "./cpu"
|
||||
|
||||
let info = Variable(get_ram_info())
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
|
||||
info.set(get_ram_info())
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
export default function Ram() {
|
||||
return <box className="status-box">
|
||||
<label label={bind(info).as((i) => `${Math.round((i.used / i.total) * 100)}% `)} />
|
||||
</box>
|
||||
}
|
||||
|
||||
15
widget/time.tsx
Normal file
15
widget/time.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { bind, GLib, Variable } from "astal"
|
||||
|
||||
let current_time = Variable(GLib.DateTime.new_now_local().format("%H:%M"))
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
|
||||
const now = GLib.DateTime.new_now_local()
|
||||
current_time.set(now.format("%b %e (%a) %H:%M"))
|
||||
return true
|
||||
})
|
||||
export default function Time() {
|
||||
|
||||
return <box className="status-box">
|
||||
<label label={bind(current_time)} />
|
||||
</box>
|
||||
}
|
||||
|
||||
16
widget/title.tsx
Normal file
16
widget/title.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { bind, Variable } from "astal"
|
||||
import AstalHyprland from "gi://AstalHyprland?version=0.1";
|
||||
|
||||
const hyprland = AstalHyprland.get_default()
|
||||
|
||||
function get_title() {
|
||||
return hyprland.focusedClient?.title ?? ""
|
||||
}
|
||||
|
||||
const title = Variable(get_title()).poll(200, () => get_title())
|
||||
|
||||
export default function Title() {
|
||||
return (
|
||||
<label label={title(t => t)} />
|
||||
)
|
||||
}
|
||||
23
widget/workspaces.tsx
Normal file
23
widget/workspaces.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { bind } from "astal"
|
||||
import { Gdk } from "astal/gtk3"
|
||||
import AstalHyprland from "gi://AstalHyprland?version=0.1"
|
||||
|
||||
export default function Workspaces({ monitor }: { monitor: Gdk.Monitor }) {
|
||||
const hypr = AstalHyprland.get_default()
|
||||
|
||||
return <box className="workspaces">
|
||||
{bind(hypr, "workspaces").as(wss => wss.filter(ws => !(ws.id >= -99 && ws.id <= -2))
|
||||
.sort((a, b) => a.id - b.id)
|
||||
.map(ws => {
|
||||
if (ws.monitor.model !== monitor.model) return <></>
|
||||
return (
|
||||
<button
|
||||
className={bind(hypr, "focusedWorkspace").as(fw =>
|
||||
ws === fw ? "focused" : "")}
|
||||
onClicked={() => ws.focus()}>
|
||||
{ws.id}
|
||||
</button>
|
||||
)
|
||||
}))}
|
||||
</box>
|
||||
}
|
||||
Reference in New Issue
Block a user