feat: split scss and rename widget folder to widgets

This commit is contained in:
tux
2025-06-09 00:41:04 +05:30
parent 9712b9e0ea
commit 72f9c24482
14 changed files with 123 additions and 119 deletions

14
widgets/bar/battery.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { bind } from "astal";
import AstalBattery from "gi://AstalBattery";
export const Battery = () => {
const battery = AstalBattery.get_default();
return (
<box cssClasses={["battery"]} visible={bind(battery, "isPresent")}>
<label
label={bind(battery, "percentage").as((p) => `${Math.floor(p * 100)}%`)}
/>
</box>
);
};

55
widgets/bar/index.tsx Normal file
View File

@@ -0,0 +1,55 @@
import { App, Astal, Gdk } from "astal/gtk4";
import { FocusedClient, WorkspaceButton } from "./workspace";
import { Battery } from "./battery";
import { Launcher } from "./launcher";
import { Tailscale } from "./tailscale";
import { Tray } from "./tray";
import { Time } from "./time";
export const WINDOW_NAME = "bar";
export const Bar = (gdkmonitor: Gdk.Monitor) => {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
return (
<window
name={WINDOW_NAME}
visible
cssClasses={["Bar"]}
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
application={App}
>
<centerbox>
<Start />
<Center />
<End />
</centerbox>
</window>
);
};
const Start = () => {
return (
<box spacing={15}>
<Launcher />
<WorkspaceButton />
</box>
);
};
const Center = () => {
return <FocusedClient />;
};
const End = () => {
return (
<box spacing={15}>
<Tailscale />
<Battery />
<Tray />
<Time />
</box>
);
};

21
widgets/bar/launcher.tsx Normal file
View File

@@ -0,0 +1,21 @@
import { App, Gtk } from "astal/gtk4";
import { Gio } from "astal";
import { Picture } from "../common";
import { WINDOW_NAME } from "../app-launcher";
export const Launcher = () => {
return (
<Gtk.ScrolledWindow heightRequest={40} widthRequest={40}>
<button
cssClasses={["launcher"]}
onClicked={() => App.toggle_window(WINDOW_NAME)}
>
<Picture
file={Gio.file_new_for_path("/home/tux/Wallpapers/avatar.png")}
contentFit={Gtk.ContentFit.CONTAIN}
overflow={Gtk.Overflow.HIDDEN}
/>
</button>
</Gtk.ScrolledWindow>
);
};

100
widgets/bar/style.scss Normal file
View File

@@ -0,0 +1,100 @@
@use "sass:math";
@use "sass:list";
window.Bar {
background: $bg-color;
color: $fg-color;
margin: 5px;
border-radius: $rounded;
padding: 10px;
.launcher {
background-color: $inactive-color;
border-radius: $rounded;
transition: all 0.15s ease-out;
padding: 0px;
margin: 0px;
&:hover {
background-color: $bg-color;
}
}
.workspace-container {
background-color: $bg-color;
color: $fg-color;
border-radius: $rounded;
.workspace-button {
padding: 0px;
border: 0px;
margin: 2px;
background-color: $inactive-color;
min-height: 1.3rem;
min-width: 1.3rem;
border-radius: calc(list.nth(8px, 1) * 1.5);
transition: min-width 0.15s ease-out;
&:hover {
background-color: $fg-color;
&.occupied {
background-color: $fg-color;
}
}
&.active {
min-width: 3.5rem;
min-height: 1.3rem;
background-color: $accent;
}
&.occupied {
background-color: $accent;
&.active {
background-color: $accent;
}
}
}
}
.focused-client {
font-size: $font-size;
font-weight: 900;
}
.tailscale {
font-size: $font-size;
font-weight: 900;
color: $bg-color;
background-color: $inactive-color;
border-radius: $rounded;
padding: 0px 10px;
}
.tray {
font-size: $font-size;
font-weight: 900;
color: #fff;
background-color: $inactive-color;
border-radius: $rounded;
padding: 0px 10px;
}
.battery {
font-size: $font-size;
font-weight: 900;
color: $bg-color;
background-color: $inactive-color;
border-radius: $rounded;
padding: 0px 10px;
}
.time {
font-size: $font-size;
font-weight: 900;
color: $bg-color;
background-color: $inactive-color;
border-radius: $rounded;
padding: 0px 10px;
}
}

20
widgets/bar/tailscale.tsx Normal file
View File

@@ -0,0 +1,20 @@
import { bind, Variable } from "astal";
export const Tailscale = () => {
const tailscale = Variable("").poll(5000, [
"bash",
"-c",
"tailscale ping vega",
]);
return (
<box cssClasses={["tailscale"]}>
<label
label={bind(tailscale).as((val) => {
const data = val.split(" ");
return data[data.length - 1];
})}
/>
</box>
);
};

12
widgets/bar/time.tsx Normal file
View File

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

18
widgets/bar/tray.tsx Normal file
View File

@@ -0,0 +1,18 @@
import { bind } from "astal";
import AstalTray from "gi://AstalTray";
export const Tray = () => {
const tray = AstalTray.get_default();
for (const item of tray.get_items()) {
print(item.title);
}
return (
<box cssClasses={["tray"]}>
{bind(tray, "items").as((items) =>
items.map((item) => <image gicon={bind(item, "gicon")} />),
)}
</box>
);
};

56
widgets/bar/workspace.tsx Normal file
View File

@@ -0,0 +1,56 @@
import { Variable, bind } from "astal";
import { Gtk } from "astal/gtk4";
import { ButtonProps } from "astal/gtk4/widget";
import AstalHyprland from "gi://AstalHyprland";
type WsButtonProps = ButtonProps & {
ws: AstalHyprland.Workspace;
};
const Workspace = ({ ws, ...props }: WsButtonProps) => {
const hyprland = AstalHyprland.get_default();
const classNames = Variable.derive(
[bind(hyprland, "focusedWorkspace"), bind(hyprland, "clients")],
(fws, _) => {
const classes = ["workspace-button"];
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()}
valign={Gtk.Align.CENTER}
halign={Gtk.Align.CENTER}
onClicked={() => ws.focus()}
{...props}
/>
);
};
export const WorkspaceButton = () => {
const range = Array.from({ length: 4 + 1 }, (_, i) => i);
return (
<box cssClasses={["workspace-container"]} spacing={4}>
{range.map((i) => (
<Workspace ws={AstalHyprland.Workspace.dummy(i + 1, null)} />
))}
</box>
);
};
export const FocusedClient = () => {
const hyprland = AstalHyprland.get_default();
const title = bind(hyprland, "focusedClient").as(
(fcsClient) => fcsClient.title,
);
return <label cssClasses={["focused-client"]}>{title}</label>;
};