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:
parent
6acfe1b0ef
commit
fcd88bfa8e
10 changed files with 225 additions and 104 deletions
|
@ -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 {};
|
|
@ -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"];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
124
home/programs/ags/notificationPopups.js
Normal file
124
home/programs/ags/notificationPopups.js
Normal 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))
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
57
home/programs/ags/style.css
Normal file
57
home/programs/ags/style.css
Normal 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;
|
||||||
|
}
|
|
@ -19,5 +19,7 @@
|
||||||
vesktop
|
vesktop
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
cinny-desktop
|
cinny-desktop
|
||||||
|
gnome-control-center
|
||||||
|
playerctl
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
13
system/services/gnome.nix
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue