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;
font-size: 1.2em;
padding: 0.5em 0;
>centerbox { centerbox {
background: $bg-color; min-height: 2.5em;
border-radius: 10px; }
margin: 8px; .Workspaces {
button {
all: unset;
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;
}
} }
button { .Battery label {
border-radius: 8px; padding-left: 0;
margin: 2px; 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(
(wifi) =>
wifi && (
<image cssClasses={["Wifi"]} iconName={bind(wifi, "iconName")} />
),
)}
</box>
);
}
return <window function Audio() {
className="Bar" const speaker = Wp.get_default()?.audio.defaultSpeaker!;
gdkmonitor={gdkmonitor}
return (
<box cssClasses={["AudioSlider"]}>
<image iconName={bind(speaker, "volumeIcon")} />
</box>
);
}
function BatteryLevel() {
const bat = Battery.get_default();
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} exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT} anchor={TOP | LEFT | RIGHT}
application={App}> >
<centerbox> <centerbox>
<button <box hexpand halign={Gtk.Align.START}>
onClicked="echo hello" <Workspaces />
halign={Gtk.Align.CENTER} </box>
> <box>
Welcome to AGS! <Time />
</button> </box>
<box /> <box cssClasses={["NotificationCenter"]} hexpand halign={Gtk.Align.END}>
<button <Audio />
onClicked={() => print("hello")} <Wifi />
halign={Gtk.Align.CENTER} <BatteryLevel />
> </box>
<label label={time()} />
</button>
</centerbox> </centerbox>
</window> </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>
);
};