feat: migrate to ags v3

This commit is contained in:
tux
2025-09-24 16:39:34 +05:30
parent 6437cad620
commit 9649ab0b6e
26 changed files with 330 additions and 451 deletions

View File

@@ -1,14 +1,15 @@
import { bind, Variable } from "astal";
import { createBinding, createComputed } from "ags";
import AstalBattery from "gi://AstalBattery";
export const Battery = () => {
const battery = AstalBattery.get_default();
const chargingIcon = Variable.derive(
[
bind(battery, "percentage"),
bind(battery, "charging"),
bind(battery, "state"),
],
const percentage = createBinding(battery, "percentage");
const charging = createBinding(battery, "charging");
const state = createBinding(battery, "state");
const chargingIcon = createComputed(
[percentage, charging, state],
(percentage, charging, state) => {
const batFull = state === AstalBattery.State.FULLY_CHARGED;
const p = percentage * 100;
@@ -21,11 +22,13 @@ export const Battery = () => {
);
return (
<box cssClasses={["pill"]} visible={bind(battery, "isPresent")} spacing={5}>
<image iconName={chargingIcon()} onDestroy={() => chargingIcon.drop()} />
<label
label={bind(battery, "percentage").as((p) => `${Math.floor(p * 100)}%`)}
/>
<box
cssClasses={["pill"]}
visible={createBinding(battery, "isPresent")}
spacing={5}
>
<image iconName={chargingIcon} />
<label label={percentage((p) => `${Math.floor(p * 100)}%`)} />
</box>
);
};

View File

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

View File

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

View File

@@ -1,14 +1,15 @@
import { App, Astal, Gdk } from "astal/gtk4";
import { FocusedClient, WorkspaceButton } from "./workspace";
import { Astal, Gdk } from "ags/gtk4";
import app from "ags/gtk4/app";
import { Battery } from "./battery";
import { Tailscale } from "./tailscale";
import { Time } from "./time";
import { CPU } from "./cpu";
import { GPU } from "./gpu";
import { Launcher } from "./launcher";
import { Network } from "./network";
import { Profile } from "./profile";
import { GPU } from "./gpu";
import { CPU } from "./cpu";
import { Launcher } from "./launcher";
import { Tailscale } from "./tailscale";
import { Time } from "./time";
import { Tray } from "./tray";
import { FocusedClient, WorkspaceButton } from "./workspace";
export const WINDOW_NAME = "bar";
@@ -23,41 +24,29 @@ export const Bar = (gdkmonitor: Gdk.Monitor) => {
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
application={App}
application={app}
>
<centerbox>
<Start />
<Center />
<End />
<box spacing={10} $type="start">
<Launcher />
<WorkspaceButton />
</box>
<box spacing={10} $type="center">
<FocusedClient />
</box>
<box spacing={10} $type="end">
<Network />
<CPU />
<GPU />
<Profile />
<Tailscale />
<Battery />
<Tray />
<Time />
</box>
</centerbox>
</window>
);
};
const Start = () => {
return (
<box spacing={10}>
<Launcher />
<WorkspaceButton />
</box>
);
};
const Center = () => {
return <FocusedClient />;
};
const End = () => {
return (
<box spacing={10}>
<Network />
<CPU />
<GPU />
<Profile />
<Tailscale />
<Battery />
<Tray />
<Time />
</box>
);
};

View File

@@ -1,12 +1,12 @@
import { App } from "astal/gtk4";
import { Gdk } from "ags/gtk4";
import app from "ags/gtk4/app";
import { WINDOW_NAME } from "../app-launcher";
import { Gdk } from "astal/gtk4";
export const Launcher = () => {
return (
<button
cssClasses={["pill", "launcher"]}
onClicked={() => App.toggle_window(WINDOW_NAME)}
onClicked={() => app.toggle_window(WINDOW_NAME)}
cursor={Gdk.Cursor.new_from_name("pointer", null)}
>
<image iconName="nix-symbolic" />

View File

@@ -1,21 +1,14 @@
import { bind } from "astal";
import AstalNetwork from "gi://AstalNetwork";
export const Network = () => {
const network = AstalNetwork.get_default();
const wifi = bind(network, "wifi");
return (
<box cssClasses={["pill"]} visible={wifi.as(Boolean)}>
{wifi.as(
(wifi) =>
wifi && (
<box spacing={5}>
<image iconName="fa-wifi-symbolic" />
<label label={bind(wifi, "ssid")} />
</box>
),
)}
<box cssClasses={["pill"]}>
<box spacing={5}>
<image iconName="fa-wifi-symbolic" />
<label label={network.wifi.ssid} />
</box>
</box>
);
};

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
import { GLib, Variable } from "astal";
import { createPoll } from "ags/time";
import GLib from "gi://GLib";
export const Time = () => {
const time = Variable("").poll(
const time = createPoll(
"",
1000,
() => GLib.DateTime.new_now_local().format("%a %b %d - %I:%M:%S %p")!,
);
return (
<label cssClasses={["pill"]} onDestroy={() => time.drop()} label={time()} />
);
return <label cssClasses={["pill"]} label={time} />;
};

View File

@@ -1,14 +1,13 @@
import { bind } from "astal";
import { createBinding, For } from "ags";
import AstalTray from "gi://AstalTray";
const tray = AstalTray.get_default();
export const Tray = () => {
const tray = AstalTray.get_default();
const items = createBinding(tray, "items");
return (
<box cssClasses={["pill", "tray"]}>
{bind(tray, "items").as((items) =>
items.map((item) => <Item item={item} />),
)}
<For each={items}>{(item) => <Item item={item} />}</For>
</box>
);
};
@@ -16,7 +15,7 @@ export const Tray = () => {
const Item = ({ item }: { item: AstalTray.TrayItem }) => {
return (
<menubutton>
<image gicon={bind(item, "gicon")} />
<image gicon={createBinding(item, "gicon")} />
</menubutton>
);
};

View File

@@ -1,37 +1,33 @@
import { Variable, bind } from "astal";
import { Gdk, Gtk } from "astal/gtk4";
import { ButtonProps } from "astal/gtk4/widget";
import { createBinding, createComputed } from "ags";
import { Gdk, Gtk } from "ags/gtk4";
import AstalHyprland from "gi://AstalHyprland";
type WsButtonProps = ButtonProps & {
type WsButtonProps = {
ws: AstalHyprland.Workspace;
};
const Workspace = ({ ws, ...props }: WsButtonProps) => {
const Workspace = ({ ws }: WsButtonProps) => {
const hyprland = AstalHyprland.get_default();
const classNames = Variable.derive(
[bind(hyprland, "focusedWorkspace"), bind(hyprland, "clients")],
(fws, _) => {
const classes = ["workspace-button"];
const focusedWorkspace = createBinding(hyprland, "focusedWorkspace");
const active = fws.id == ws.id;
active && classes.push("active");
const classNames = createComputed([focusedWorkspace], (fws) => {
const classes = ["workspace-button"];
const occupied = hyprland.get_workspace(ws.id)?.get_clients().length > 0;
occupied && classes.push("occupied");
return classes;
},
);
const active = fws.id == ws.id;
active && classes.push("active");
const occupied = hyprland.get_workspace(ws.id)?.get_clients().length > 0;
occupied && classes.push("occupied");
return classes;
});
return (
<button
cssClasses={classNames()}
onDestroy={() => classNames.drop()}
cssClasses={classNames}
valign={Gtk.Align.CENTER}
halign={Gtk.Align.CENTER}
onClicked={() => ws.focus()}
cursor={Gdk.Cursor.new_from_name("pointer", null)}
{...props}
/>
);
};
@@ -49,14 +45,11 @@ export const WorkspaceButton = () => {
export const FocusedClient = () => {
const hyprland = AstalHyprland.get_default();
const focused = bind(hyprland, "focusedClient");
const focused = createBinding(hyprland, "focusedClient");
return (
<box cssClasses={["focused-client"]} visible={focused.as(Boolean)}>
{focused.as(
(client) =>
client && <label label={bind(client, "initialTitle").as(String)} />,
)}
<label label={focused((client) => client.title)} />
</box>
);
};