refactor: move system info to control center

This commit is contained in:
tux
2025-09-26 03:50:07 +05:30
parent b745b16ca5
commit be1f269fff
12 changed files with 207 additions and 98 deletions

View File

@@ -1,16 +0,0 @@
import { createPoll } from "ags/time";
export const CPU = () => {
const cpu = createPoll("", 5000, [
"bash",
"-c",
"cat /sys/class/thermal/thermal_zone*/temp",
]);
return (
<box cssClasses={["pill"]} spacing={5}>
<image iconName="fa-cpu-symbolic" />
<label label={cpu((val) => `${parseInt(val) / 1000} °C`)} />
</box>
);
};

View File

@@ -1,12 +0,0 @@
import { createPoll } from "ags/time";
export const GPU = () => {
const gpu = createPoll("", 5000, ["bash", "-c", "supergfxctl -g"]);
return (
<box cssClasses={["pill"]} spacing={5}>
<image iconName="fa-video-card-symbolic" />
<label label={gpu((val) => val)} />
</box>
);
};

View File

@@ -1,12 +1,8 @@
import { Astal, Gdk } from "ags/gtk4";
import app from "ags/gtk4/app";
import { Battery } from "./battery";
import { CPU } from "./cpu";
import { GPU } from "./gpu";
import { Launcher } from "./launcher";
import { Network } from "./network";
import { Profile } from "./profile";
import { Tailscale } from "./tailscale";
import { Time } from "./time";
import { Tray } from "./tray";
import { WorkspaceButton } from "./workspace";
@@ -14,7 +10,7 @@ import { WorkspaceButton } from "./workspace";
export const WINDOW_NAME = "bar";
export const Bar = (gdkmonitor: Gdk.Monitor) => {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
const { TOP } = Astal.WindowAnchor;
return (
<window
@@ -23,16 +19,15 @@ export const Bar = (gdkmonitor: Gdk.Monitor) => {
cssClasses={["Bar"]}
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
anchor={TOP}
widthRequest={1200}
application={app}
>
<centerbox>
<box spacing={10} $type="start">
<Launcher windowName="launcher" icon="nix-symbolic" />
<CPU />
<GPU />
<Profile />
<Tailscale />
<Network />
<Battery />
</box>
<box spacing={10} $type="center">
@@ -40,8 +35,6 @@ export const Bar = (gdkmonitor: Gdk.Monitor) => {
</box>
<box spacing={10} $type="end">
<Network />
<Battery />
<Tray />
<Time />
<Launcher windowName="control-center" icon="fa-ghost-symbolic" />

View File

@@ -1,17 +0,0 @@
import { createPoll } from "ags/time";
export const Profile = () => {
const profile = createPoll("", 5000, ["bash", "-c", "asusctl profile -p"]);
return (
<box cssClasses={["pill"]} spacing={5}>
<image iconName="fa-speed-symbolic" />
<label
label={profile((val) => {
const data = val.split(" ");
return data[data.length - 1];
})}
/>
</box>
);
};

View File

@@ -1,22 +0,0 @@
import { createPoll } from "ags/time";
export const Tailscale = () => {
const tailscale = createPoll("", 5000, [
"bash",
"-c",
"tailscale ping homelab",
]);
return (
<box cssClasses={["pill"]} spacing={5}>
<image iconName="fa-home-symbolic" />
<label
label={tailscale((val) => {
const data = val.split(" ");
return data[data.length - 1];
})}
/>
</box>
);
};

View File

@@ -5,7 +5,7 @@ export const Time = () => {
const time = createPoll(
"",
1000,
() => GLib.DateTime.new_now_local().format("%a %b %d - %I:%M:%S %p")!,
() => GLib.DateTime.new_now_local().format("%I:%M %p")!,
);
return <label cssClasses={["pill"]} label={time} />;

View File

@@ -1,11 +1,18 @@
import { Gdk, Gtk } from "ags/gtk4";
import { exec } from "ags/process";
import { execAsync } from "ags/process";
import { createPoll } from "ags/time";
import GLib from "gi://GLib";
export const Header = () => {
const { VERTICAL } = Gtk.Orientation;
const { CENTER, START, END } = Gtk.Align;
const time = createPoll(
"",
1000,
() => GLib.DateTime.new_now_local().format("%a %b %d - %I:%M:%S %p")!,
);
return (
<box cssClasses={["header"]}>
<image
@@ -19,20 +26,38 @@ export const Header = () => {
<label halign={START} label="@tuxdotrs" />
</box>
<box hexpand halign={END} spacing={10} cssClasses={["controls"]}>
<button
cursor={Gdk.Cursor.new_from_name("pointer", null)}
onClicked={() => exec("hyprlock")}
>
<image iconName="fa-lock-symbolic" />
</button>
<box
hexpand
halign={END}
valign={CENTER}
spacing={10}
cssClasses={["controls"]}
orientation={VERTICAL}
>
<box spacing={10} homogeneous>
<button
cursor={Gdk.Cursor.new_from_name("pointer", null)}
onClicked={() => execAsync("flameshot launcher")}
>
<image iconName="fa-screenshot-symbolic" />
</button>
<button
cursor={Gdk.Cursor.new_from_name("pointer", null)}
onClicked={() => exec("poweroff")}
>
<image iconName="fa-power-symbolic" />
</button>
<button
cursor={Gdk.Cursor.new_from_name("pointer", null)}
onClicked={() => execAsync("hyprlock")}
>
<image iconName="fa-lock-symbolic" />
</button>
<button
cursor={Gdk.Cursor.new_from_name("pointer", null)}
onClicked={() => execAsync("poweroff")}
>
<image iconName="fa-power-symbolic" />
</button>
</box>
<label label={time} />
</box>
</box>
);

View File

@@ -4,6 +4,7 @@ import { Footer } from "./footer";
import { Header } from "./header";
import { NotificationList } from "./notification-list";
import { SlidingControls } from "./sliding-controls";
import { SystemInfo } from "./system-info";
export const WINDOW_NAME = "control-center";
@@ -14,6 +15,7 @@ export const ControlCenter = (gdkmonitor: Gdk.Monitor) => {
return (
<window
name={WINDOW_NAME}
visible
cssClasses={["control-center"]}
gdkmonitor={gdkmonitor}
application={app}
@@ -24,6 +26,7 @@ export const ControlCenter = (gdkmonitor: Gdk.Monitor) => {
<box vexpand orientation={VERTICAL} spacing={20}>
<Header />
<SlidingControls />
<SystemInfo />
<NotificationList />
<Footer />
</box>

View File

@@ -0,0 +1,143 @@
import { createBinding, createComputed } from "ags";
import { Gtk } from "ags/gtk4";
import { createPoll } from "ags/time";
import AstalBattery from "gi://AstalBattery";
import AstalNetwork from "gi://AstalNetwork";
export const SystemInfo = () => {
const cpu = PollCMD("cat /sys/class/thermal/thermal_zone*/temp");
const gpu = PollCMD("supergfxctl -g");
const profile = PollCMD("asusctl profile -p");
const tailscale = PollCMD("tailscale ping homelab");
const battery = AstalBattery.get_default();
const percentage = createBinding(battery, "percentage");
const charging = createBinding(battery, "charging");
const state = createBinding(battery, "state");
const energyRate = createBinding(battery, "energyRate");
const chargingIcon = createComputed(
[percentage, charging, state],
(percentage, charging, state) => {
const batFull = state === AstalBattery.State.FULLY_CHARGED;
const p = percentage * 100;
if (batFull) return "fa-battery-full-symbolic";
if (charging) return "fa-battery-charging-symbolic";
if (p < 30) return "fa-battery-low-symbolic";
return p < 70 ? "fa-battery-medium-symbolic" : "fa-battery-full-symbolic";
},
);
const network = AstalNetwork.get_default();
const connnectivity = createBinding(network, "connectivity");
const getNetworkText = (
conn: AstalNetwork.Connectivity,
net: AstalNetwork.Network,
) => {
// no connection
if (conn == 1) return "No connection";
// wired
if (net.primary == 1) return "Wired";
// wifi
const wifi = net.wifi;
switch (wifi.internet) {
case 0:
return wifi.ssid;
case 1:
return "Connecting";
case 2:
return "Disconnected";
}
return "NA";
};
return (
<>
<box hexpand homogeneous spacing={20} cssClasses={["system-info"]}>
<box
cssClasses={["pill"]}
spacing={15}
orientation={Gtk.Orientation.VERTICAL}
>
<image iconName="fa-wifi-symbolic" />
<label label={connnectivity((c) => getNetworkText(c, network))} />
</box>
<box
hexpand
cssClasses={["pill"]}
spacing={15}
orientation={Gtk.Orientation.VERTICAL}
>
<image iconName="fa-video-card-symbolic" />
<label label={gpu((val) => val)} />
</box>
<box
hexpand
cssClasses={["pill"]}
spacing={15}
orientation={Gtk.Orientation.VERTICAL}
>
<image iconName="fa-speed-symbolic" />
<label
label={profile((val) => {
const data = val.split(" ");
return data[data.length - 1];
})}
/>
</box>
</box>
<box hexpand homogeneous spacing={20} cssClasses={["system-info"]}>
<box
hexpand
cssClasses={["pill"]}
spacing={15}
orientation={Gtk.Orientation.VERTICAL}
>
<image iconName="fa-cpu-symbolic" />
<label label={cpu((val) => `${parseInt(val) / 1000} °C`)} />
</box>
<box
cssClasses={["pill"]}
spacing={15}
orientation={Gtk.Orientation.VERTICAL}
>
<image iconName="fa-home-symbolic" />
<label
label={tailscale((val) => {
const data = val.split(" ");
return data[data.length - 1];
})}
/>
</box>
<box
cssClasses={["pill"]}
visible={createBinding(battery, "isPresent")}
spacing={15}
orientation={Gtk.Orientation.VERTICAL}
>
<image iconName={chargingIcon} />
<box hexpand halign={Gtk.Align.CENTER} spacing={5}>
<label label={percentage((p) => `${Math.floor(p * 100)}%`)} />
<label label={energyRate((v) => `${Math.floor(v * 10) / 10}W`)} />
</box>
</box>
</box>
</>
);
};
const PollCMD = (cmd: string, ms: number = 5000) => {
return createPoll("", ms, ["bash", "-c", cmd]);
};