feat: add basic bar

This commit is contained in:
Anthony Rodriguez 2025-02-11 17:51:09 +01:00
parent 864433e39a
commit af86cc7beb
Signed by: nezia
SSH key fingerprint: SHA256:R/ue1eTzTHUoo77lJD/3fSUsyL4AwvcHImU5BAZai+8
6 changed files with 170 additions and 54 deletions

6
app.ts
View file

@ -1,10 +1,8 @@
import { App } from "astal/gtk3"; import { App } from "astal/gtk4";
import style from "./style.scss"; import style from "./style.scss";
import Bar from "./widget/Bar"; import Bar from "./widget/Bar";
App.start({ App.start({
css: style, css: style,
main() { main: () => App.get_monitors().map(Bar),
App.get_monitors().map(Bar);
},
}); });

View file

@ -17,6 +17,14 @@
}: let }: let
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
astalPkgs = with ags.packages.${system}; [
battery
hyprland
mpris
network
tray
wireplumber
];
in { in {
packages.${system} = { packages.${system} = {
default = ags.lib.bundle { default = ags.lib.bundle {
@ -24,12 +32,10 @@
src = ./.; src = ./.;
name = "coquille"; name = "coquille";
entry = "app.ts"; entry = "app.ts";
gtk4 = true;
# additional libraries and executables to add to gjs' runtime # additional libraries and executables to add to gjs' runtime
extraPackages = [ extraPackages = astalPkgs;
# ags.packages.${system}.battery
# pkgs.fzf
];
}; };
}; };
@ -38,9 +44,7 @@
buildInputs = [ buildInputs = [
# includes astal3 astal4 astal-io by default # includes astal3 astal4 astal-io by default
(ags.packages.${system}.default.override { (ags.packages.${system}.default.override {
extraPackages = [ extraPackages = astalPkgs;
# cherry pick packages
];
}) })
]; ];
}; };

View file

@ -1,20 +1,56 @@
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss @use "sass:color";
$fg-color: #{"@theme_fg_color"}; @use "sass:string";
$bg-color: #{"@theme_bg_color"};
@function gtkalpha($c, $a) {
@return string.unquote("alpha(#{$c},#{$a})");
}
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/src/stylesheet/_colors.scss
$bg: #{"@window_bg_color"};
$fg: #{"@window_fg_color"};
$accent: #{"@accent_color"};
$radius: 7px;
window.Bar { window.Bar {
background: transparent; border: none;
color: $fg-color; box-shadow: none;
font-weight: bold; background-color: $bg;
color: $fg;
>centerbox { font-size: 1.2em;
background: $bg-color; padding: 0.5em 0;
border-radius: 10px;
margin: 8px;
}
centerbox {
min-height: 2.5em;
}
.Workspaces {
button { button {
border-radius: 8px; all: unset;
margin: 2px; min-width: 1.1rem;
min-height: 1.1rem;
background-color: transparent;
border-radius: 50%;
border: 2px solid $fg;
margin: 0.25rem 0.3rem;
&:hover {
border-color: gtkalpha($accent, 0.6);
}
} }
.focused {
border-color: $accent;
background-color: $accent;
}
}
.Battery label {
padding-left: 0;
margin-left: 0;
}
.NotificationCenter {
&:hover {
background-color: gtkalpha($fg, 0.2);
}
}
} }

View file

@ -3,7 +3,7 @@
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true, "experimentalDecorators": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "astal/gtk3", "jsxImportSource": "astal/gtk4",
"module": "ES2022", "module": "ES2022",
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"strict": true, "strict": true,

View file

@ -1,31 +1,85 @@
import { App, Astal, Gtk, Gdk } from "astal/gtk3" import { App, Astal, Gtk, Gdk } from "astal/gtk4";
import { Variable } from "astal" import { Variable, GLib, bind } from "astal";
import Battery from "gi://AstalBattery";
import Wp from "gi://AstalWp";
import Network from "gi://AstalNetwork";
import { Workspaces } from "./Workspaces";
const time = Variable("").poll(1000, "date") function Wifi() {
const network = Network.get_default();
const wifi = bind(network, "wifi");
export default function Bar(gdkmonitor: Gdk.Monitor) { return (
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor <box visible={wifi.as(Boolean)}>
{wifi.as(
return <window (wifi) =>
className="Bar" wifi && (
gdkmonitor={gdkmonitor} <image cssClasses={["Wifi"]} iconName={bind(wifi, "iconName")} />
exclusivity={Astal.Exclusivity.EXCLUSIVE} ),
anchor={TOP | LEFT | RIGHT} )}
application={App}> </box>
<centerbox> );
<button }
onClicked="echo hello"
halign={Gtk.Align.CENTER} function Audio() {
> const speaker = Wp.get_default()?.audio.defaultSpeaker!;
Welcome to AGS!
</button> return (
<box /> <box cssClasses={["AudioSlider"]}>
<button <image iconName={bind(speaker, "volumeIcon")} />
onClicked={() => print("hello")} </box>
halign={Gtk.Align.CENTER} );
> }
<label label={time()} />
</button> function BatteryLevel() {
</centerbox> const bat = Battery.get_default();
</window>
return (
<box cssClasses={["Battery"]} visible={bind(bat, "isPresent")}>
<image iconName={bind(bat, "batteryIconName")} />
<label
label={bind(bat, "percentage").as((p) => `${Math.floor(p * 100)} %`)}
/>
</box>
);
}
function Time({ format = "%H:%M - %A %e." }) {
const time = Variable<string>("").poll(
1000,
() => GLib.DateTime.new_now_local().format(format)!,
);
return (
<label cssClasses={["Time"]} onDestroy={() => time.drop()} label={time()} />
);
}
export default function Bar(monitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
return (
<window
visible
namespace={"bar"}
cssClasses={["Bar"]}
gdkmonitor={monitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
>
<centerbox>
<box hexpand halign={Gtk.Align.START}>
<Workspaces />
</box>
<box>
<Time />
</box>
<box cssClasses={["NotificationCenter"]} hexpand halign={Gtk.Align.END}>
<Audio />
<Wifi />
<BatteryLevel />
</box>
</centerbox>
</window>
);
} }

24
widget/Workspaces.tsx Normal file
View file

@ -0,0 +1,24 @@
import { bind } from "astal";
import Hyprland from "gi://AstalHyprland";
export const Workspaces = () => {
const hypr = Hyprland.get_default();
return (
<box cssClasses={["Workspaces"]}>
{bind(hypr, "workspaces").as((wss) =>
wss
.filter((ws) => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces
.sort((a, b) => a.id - b.id)
.map((ws) => (
<button
cssClasses={bind(hypr, "focusedWorkspace").as((fw) =>
ws === fw ? ["focused"] : [""],
)}
onClicked={() => ws.focus()}
></button>
)),
)}
</box>
);
};