repo: implement ags

Implement ags, a wayland bar / GTK shell. Also added a few dependencies
required for it to work properly.
This commit is contained in:
Anthony Rodriguez 2024-10-09 19:22:20 +02:00
parent 6acfe1b0ef
commit fcd88bfa8e
Signed by: nezia
GPG key ID: EE3BE97C040A86CE
10 changed files with 225 additions and 104 deletions

View file

@ -1,10 +1,8 @@
// const hyprland = await Service.import("hyprland");
const notifications = await Service.import("notifications");
const mpris = await Service.import("mpris"); const mpris = await Service.import("mpris");
const audio = await Service.import("audio"); const audio = await Service.import("audio");
const battery = await Service.import("battery"); const battery = await Service.import("battery");
const systemtray = await Service.import("systemtray"); const systemtray = await Service.import("systemtray");
import { NotificationPopups } from "./notificationPopups.js";
const date = Variable("", { const date = Variable("", {
poll: [1000, 'date "+%H:%M %b %e."'], poll: [1000, 'date "+%H:%M %b %e."'],
}); });
@ -13,31 +11,6 @@ const date = Variable("", {
// so to make a reuseable widget, make it a function // so to make a reuseable widget, make it a function
// then you can simply instantiate one by calling it // then you can simply instantiate one by calling it
//function Workspaces() {
// const activeId = hyprland.active.workspace.bind("id");
// const workspaces = hyprland.bind("workspaces").as((ws) =>
// ws.map(({ id }) =>
// Widget.Button({
// on_clicked: () => hyprland.messageAsync(`dispatch workspace ${id}`),
// child: Widget.Label(`${id}`),
// class_name: activeId.as((i) => `${i === id ? "focused" : ""}`),
// }),
// ),
// );
//
// return Widget.Box({
// class_name: "workspaces",
// children: workspaces,
// });
//}
function ClientTitle() {
return Widget.Label({
class_name: "client-title",
label: "Label!", // hyprland.active.client.bind("title"),
});
}
function Clock() { function Clock() {
return Widget.Label({ return Widget.Label({
class_name: "clock", class_name: "clock",
@ -45,24 +18,6 @@ function Clock() {
}); });
} }
// we don't need dunst or any other notification daemon
// because the Notifications module is a notification daemon itself
function Notification() {
const popups = notifications.bind("popups");
return Widget.Box({
class_name: "notification",
visible: popups.as((p) => p.length > 0),
children: [
Widget.Icon({
icon: "preferences-system-notifications-symbolic",
}),
Widget.Label({
label: popups.as((p) => p[0]?.summary || ""),
}),
],
});
}
function Media() { function Media() {
const label = Utils.watch("", mpris, "player-changed", () => { const label = Utils.watch("", mpris, "player-changed", () => {
if (mpris.players[0]) { if (mpris.players[0]) {
@ -161,19 +116,13 @@ function SysTray() {
// layout of the bar // layout of the bar
function Left() { function Left() {
return Widget.Box({ return Widget.Box({});
spacing: 8,
children: [
// Workspaces(),
ClientTitle(),
],
});
} }
function Center() { function Center() {
return Widget.Box({ return Widget.Box({
spacing: 8, spacing: 8,
children: [Media(), Notification()], children: [Media()],
}); });
} }
@ -202,13 +151,7 @@ function Bar(monitor = 0) {
App.config({ App.config({
style: "./style.css", style: "./style.css",
windows: [ windows: [Bar(), NotificationPopups()],
Bar(),
// you can call it, for each monitor
// Bar(0),
// Bar(1)
],
}); });
export {}; export {};

View file

@ -1,15 +1,23 @@
{ {
inputs, inputs,
pkgs, pkgs,
config,
... ...
}: { }: let
cfg = config.programs.ags;
in {
imports = [inputs.ags.homeManagerModules.default]; imports = [inputs.ags.homeManagerModules.default];
home.packages = with pkgs; [
libdbusmenu-gtk3
dart-sass
];
programs.ags = { programs.ags = {
enable = true; enable = true;
# null or path, leave as null if you don't want hm to manage the config # null or path, leave as null if you don't want hm to manage the config
configDir = ./src; configDir = ./.;
# additional packages to add to gjs's runtime # additional packages to add to gjs's runtime
extraPackages = with pkgs; [ extraPackages = with pkgs; [
@ -18,4 +26,17 @@
accountsservice accountsservice
]; ];
}; };
systemd.user.services.ags = {
Unit = {
Description = "Aylur's Gtk Shell";
PartOf = [
"graphical-session.target"
];
};
Service = {
ExecStart = "${cfg.package}/bin/ags";
Restart = "on-failure";
};
Install.WantedBy = ["graphical-session.target"];
};
} }

View file

@ -0,0 +1,124 @@
const notifications = await Service.import("notifications");
/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */
function NotificationIcon({ app_entry, app_icon, image }) {
if (image) {
return Widget.Box({
css:
`background-image: url("${image}");` +
"background-size: contain;" +
"background-repeat: no-repeat;" +
"background-position: center;",
});
}
let icon = "dialog-information-symbolic";
if (Utils.lookUpIcon(app_icon)) icon = app_icon;
if (app_entry && Utils.lookUpIcon(app_entry)) icon = app_entry;
return Widget.Box({
child: Widget.Icon(icon),
});
}
/** @param {import('resource:///com/github/Aylur/ags/service/notifications.js').Notification} n */
function Notification(n) {
const icon = Widget.Box({
vpack: "start",
class_name: "icon",
child: NotificationIcon(n),
});
const title = Widget.Label({
class_name: "title",
xalign: 0,
justification: "left",
hexpand: true,
max_width_chars: 24,
truncate: "end",
wrap: true,
label: n.summary,
use_markup: true,
});
const body = Widget.Label({
class_name: "body",
hexpand: true,
use_markup: true,
xalign: 0,
justification: "left",
label: n.body,
wrap: true,
});
const actions = Widget.Box({
class_name: "actions",
children: n.actions.map(({ id, label }) =>
Widget.Button({
class_name: "action-button",
on_clicked: () => {
n.invoke(id);
n.dismiss();
},
hexpand: true,
child: Widget.Label(label),
}),
),
});
return Widget.EventBox(
{
attribute: { id: n.id },
on_primary_click: n.dismiss,
},
Widget.Box(
{
class_name: `notification ${n.urgency}`,
vertical: true,
},
Widget.Box([icon, Widget.Box({ vertical: true }, title, body)]),
actions,
),
);
}
export function NotificationPopups(monitor = 0) {
const list = Widget.Box({
vertical: true,
children: notifications.popups.map(Notification),
});
function onNotified(_, /** @type {number} */ id) {
const n = notifications.getNotification(id);
if (n) list.children = [Notification(n), ...list.children];
}
function onDismissed(_, /** @type {number} */ id) {
list.children.find((n) => n.attribute.id === id)?.destroy();
}
list
.hook(notifications, onNotified, "notified")
.hook(notifications, onDismissed, "dismissed");
return Widget.Window({
monitor,
name: `notifications${monitor}`,
class_name: "notification-popups",
anchor: ["top", "right"],
child: Widget.Box({
css: "min-width: 2px; min-height: 2px;",
class_name: "notifications",
vertical: true,
child: list,
/** this is a simple one liner that could be used instead of
hooking into the 'notified' and 'dismissed' signals.
but its not very optimized becuase it will recreate
the whole list everytime a notification is added or dismissed */
// children: notifications.bind('popups')
// .as(popups => popups.map(Notification))
}),
});
}

View file

@ -1,40 +0,0 @@
window.bar {
background-color: @theme_bg_color;
color: @theme_fg_color;
}
button {
min-width: 0;
padding-top: 0;
padding-bottom: 0;
background-color: transparent;
}
button:active {
background-color: @theme_selected_bg_color;
}
button:hover {
border-bottom: 3px solid @theme_fg_color;
}
label {
font-weight: bold;
}
.workspaces button.focused {
border-bottom: 3px solid @theme_selected_bg_color;
}
.client-title {
color: @theme_selected_bg_color;
}
.notification {
color: yellow;
}
levelbar block,
highlight {
min-height: 10px;
}

View file

@ -0,0 +1,57 @@
window.notification-popups box.notifications {
padding: 0.5em;
}
.icon {
min-width: 68px;
min-height: 68px;
margin-right: 1em;
}
.icon image {
font-size: 58px;
/* to center the icon */
margin: 5px;
color: @theme_fg_color;
}
.icon box {
min-width: 68px;
min-height: 68px;
border-radius: 7px;
}
.notification {
min-width: 350px;
border-radius: 11px;
padding: 1em;
margin: 0.5em;
border: 1px solid @wm_borders_edge;
background-color: @theme_bg_color;
}
.notification.critical {
border: 1px solid lightcoral;
}
.title {
color: @theme_fg_color;
font-size: 1.4em;
}
.body {
color: @theme_unfocused_fg_color;
}
.actions .action-button {
margin: 0 0.4em;
margin-top: 0.8em;
}
.actions .action-button:first-child {
margin-left: 0;
}
.actions .action-button:last-child {
margin-right: 0;
}

View file

@ -19,5 +19,7 @@
vesktop vesktop
wl-clipboard wl-clipboard
cinny-desktop cinny-desktop
gnome-control-center
playerctl
]; ];
} }

View file

@ -19,8 +19,9 @@
++ [ ++ [
./vamos ./vamos
../system/services/regreet.nix "${mod}/services/regreet.nix"
"${mod}/programs/niri" "${mod}/programs/niri"
"${mod}/services/gnome.nix"
self.nixosModules.style self.nixosModules.style

13
system/services/gnome.nix Normal file
View file

@ -0,0 +1,13 @@
{pkgs, ...}: {
services = {
# needed for GNOME services outside of GNOME Desktop
dbus.packages = with pkgs; [
gcr
gnome-settings-daemon
];
gnome.gnome-keyring.enable = true;
gvfs.enable = true;
};
}