ags is finally mostly setup...

This commit is contained in:
Chris Cochrun 2024-06-25 06:50:44 -05:00
parent 3eda03e1d8
commit 7311597752
18 changed files with 26906 additions and 202 deletions

View file

@ -1,19 +0,0 @@
#!/usr/bin/env bash
set -e
if [[ ! -d "/home/chris/.dotfiles/.config/ags" ]]; then
echo "Cannot find source directory; Did you move it?"
echo "(Looking for "/home/chris/.dotfiles/.config/ags")"
echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
exit 1
fi
# rebuild the cache forcefully
_nix_direnv_force_reload=1 direnv exec "/home/chris/.dotfiles/.config/ags" true
# Update the mtime for .envrc.
# This will cause direnv to reload again - but without re-building.
touch "/home/chris/.dotfiles/.config/ags/.envrc"
# Also update the timestamp of whatever profile_rc we have.
# This makes sure that we know we are up to date.
touch -r "/home/chris/.dotfiles/.config/ags/.envrc" "/home/chris/.dotfiles/.config/ags/.direnv"/*.rc

View file

@ -1 +0,0 @@
use flake

View file

@ -1,8 +0,0 @@
(ns ags-config)
(defn bar [monitor]
(let [my-label (Widget.Label.
(clj->js {
:label "some example content"
}))])
)

View file

@ -1,11 +1,15 @@
const css = `${App.configDir}/style.css`; const css = `${App.configDir}/style.css`;
const hyprland = await Service.import("hyprland"); const hyprland = await Service.import("hyprland");
// const systray = await Service.import("systemtray"); const systray = await Service.import("systemtray");
const battery = await Service.import("battery");
const audio = await Service.import('audio')
import { NotificationPopups } from "./notifications.js"
function workspaces() { function workspaces() {
const active = hyprland.active.workspace.bind("id"); const active = hyprland.active.workspace.bind("id");
const workspaces = hyprland.bind("workspaces") const workspaces = hyprland.bind("workspaces")
.as(ws => ws.map(({ id }) => Widget.Button({ .as(ws => ws.map(({ id }) => id === -99 ? "" : Widget.Button({
onClicked: () => hyprland.messageAsync(`dispatch workspace ${id}`), onClicked: () => hyprland.messageAsync(`dispatch workspace ${id}`),
child: Widget.Label(`${id}`), child: Widget.Label(`${id}`),
class_name: active.as(i => `${i === id ? "focused" : ""}`), class_name: active.as(i => `${i === id ? "focused" : ""}`),
@ -16,27 +20,121 @@ function workspaces() {
}) })
} }
function client_name() {
const client_class = hyprland.active.client.bind("class");
return Widget.Box({
class_name: "window-title-box",
spacing: 6,
children: [
Widget.Icon({
class_name: "client-icon",
icon: client_class.as(c => `${c === "ff" ? "firefox" : c}`),
}),
Widget.Label({
class_name: "client-title",
label: hyprland.active.client.bind("title"),
maxWidthChars: 54,
truncate: "end",
}),
],
})
}
function system_tray() {
/** @param {import('types/service/systemtray').TrayItem} item */
const systray_item = item => Widget.Button({
child: Widget.Icon().bind('icon', item, 'icon'),
tooltipMarkup: item.bind('tooltip_markup'),
onPrimaryClick: (_, event) => item.activate(event),
onSecondaryClick: (_, event) => item.openMenu(event),
});
return Widget.Box({
class_name: "systemtray",
children: systray.bind("items").as(i => i.map(systray_item)),
})
}
function battery_function() {
const bat = battery.bind("percent");
const charging = battery.bind("charging");
const time_left = battery.bind("time-remaining");
return Widget.CircularProgress({
start_at: 0.75,
rounded: true,
value: bat.as(p => p / 100),
class_name: battery.bind("charging").as(c => c ? "battery_dial_charging" : "battery_dial"),
child: Widget.Icon({
class_name: "battery_icon",
icon: battery.bind("icon-name"),
}),
tooltip_text: time_left.as(t => "Time till full charge: " + t),
})
}
const date = Variable("", { const date = Variable("", {
poll: [1000, 'date "+\%a \%b \%d, \%-I:\%M \%p"'], poll: [1000, 'date "+\%a \%b \%d, \%-I:\%M \%p"'],
}) })
const expander = Widget.Label({
hexpand: true,
label: "",
})
const volume_indicator = Widget.Button({
on_clicked: () => audio.speaker.is_muted = !audio.speaker.is_muted,
child: Widget.Icon().hook(audio.speaker, self => {
const vol = audio.speaker.volume * 100;
const icon = [
[101, 'overamplified'],
[67, 'high'],
[34, 'medium'],
[1, 'low'],
[0, 'muted'],
].find(([threshold]) => threshold <= vol)?.[1];
self.icon = `audio-volume-${icon}-symbolic`;
self.tooltip_text = `Volume ${Math.floor(vol)}%`;
}),
})
function Bar(monitor = 0) { function Bar(monitor = 0) {
const myLabel = Widget.Label({ const myLabel = Widget.Label({
label: 'some example content', label: 'some example content',
}) })
const clock = Widget.Label({ const clock = Widget.Label({
class_name: "clock",
label: date.bind(), label: date.bind(),
truncate: "end",
}) })
return Widget.Window({ return Widget.Window({
monitor, monitor,
name: `bar${monitor}`, // this name has to be unique name: `bar${monitor}`, // this name has to be unique
anchor: ['top', 'left', 'right'], anchor: ['bottom', 'left', 'right'],
exclusivity: "exclusive",
margins: [0, 20, 8, 20],
child: Widget.CenterBox({ child: Widget.CenterBox({
startWidget: workspaces(), class_name: "windowbox",
startWidget: Widget.Box({
spacing: 0,
children: [
workspaces(),
client_name(),
],
}),
centerWidget: clock, centerWidget: clock,
endWidget: myLabel, endWidget: Widget.Box({
hexpand: true,
css: "margin-right: 1em;",
children: [
expander,
volume_indicator,
system_tray(),
battery_function(),
],
}),
}), }),
}) })
} }
@ -45,8 +143,7 @@ App.config({
style: css, style: css,
windows: [ windows: [
Bar(0), // can be instantiated for each monitor Bar(0), // can be instantiated for each monitor
Bar(1), NotificationPopups(),
], ],
}) })
export { }

View file

@ -1,20 +0,0 @@
function Bar(monitor = 0) {
const myLabel = Widget.Label({
label: 'some example content',
})
return Widget.Window({
monitor,
name: `bar${monitor}`, // this name has to be unique
anchor: ['top', 'left', 'right'],
child: myLabel,
})
}
App.config({
windows: [
Bar(0), // can be instantiated for each monitor
Bar(1),
Bar(2),
],
})

View file

@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1718318537,
"narHash": "sha256-4Zu0RYRcAY/VWuu6awwq4opuiD//ahpc2aFHg2CWqFY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "e9ee548d90ff586a6471b4ae80ae9cfcbceb3420",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,41 +0,0 @@
{
description = "The Flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = inputs: with inputs;
flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = import nixpkgs {
inherit system;
};
nativeBuildInputs = with pkgs; [
];
buildInputs = with pkgs; [
stdenv
clojure
clojure-lsp
clj-kondo
leiningen
];
nativeLibs = with pkgs; [
];
in rec
{
devShell = pkgs.mkShell {
nativeBuildInputs = nativeBuildInputs;
buildInputs = buildInputs;
nativeLibs = nativeLibs;
};
defaultPackage = pkgs.libsForQt5.callPackage ./default.nix { };
}
);
}

26466
.config/ags/main.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,130 @@
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: "notif-icon",
child: NotificationIcon(n),
})
const title = Widget.Label({
class_name: "notif-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: "notif-body",
hexpand: true,
use_markup: true,
xalign: 0,
justification: "left",
label: n.body,
wrap: true,
})
const actions = Widget.Box({
class_name: "notif-actions",
children: n.actions.map(({ id, label }) => Widget.Button({
class_name: "notif-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 = [...list.children, Notification(n)]
}
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: ["bottom", "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

@ -9,6 +9,6 @@
; The standard ClojureScript compiler options: ; The standard ClojureScript compiler options:
; (See the ClojureScript compiler documentation for details.) ; (See the ClojureScript compiler documentation for details.)
:compiler { :compiler {
:output-to "config.js" ; default: target/cljsbuild-main.js :output-to "main.js" ; default: target/cljsbuild-main.js
:optimizations :simple :optimizations :simple
:pretty-print true}}]}) :pretty-print true}}]})

View file

@ -1,15 +0,0 @@
{ pkgs ? import <nixpkgs> { } }:
with pkgs;
mkShell rec {
name = "ags-config";
nativeBuildInputs = [
];
buildInputs = [
stdenv
clojure
clojure-lsp
clj-kondo
];
}

View file

@ -4,5 +4,4 @@
(let [my-label (Widget.Label. (let [my-label (Widget.Label.
(clj->js { (clj->js {
:label "some example content" :label "some example content"
}))]) }))]))
)

View file

@ -1,21 +1,171 @@
@define-color base00 #282a36;
@define-color base01 #34353e;
@define-color base02 #43454f;
@define-color base03 #78787e;
@define-color base04 #a5a5a9;
@define-color base05 #e2e4e5;
@define-color base06 #eff0eb;
@define-color base07 #f1f1f0;
@define-color base08 #ff5c57;
@define-color base09 #ff9f43;
@define-color base0A #f3f99d;
@define-color base0B #5af78e;
@define-color base0C #9aedfe;
@define-color base0D #57c7ff;
@define-color base0E #ff6ac1;
@define-color base0F #b2643c;
@define-color basetransparent rgba(40, 42, 54, 0.0);
@define-color backtransparent rgba(40, 42, 54, 0.80);
window { window {
background-color: transparent; background-color: @basetransparent;
}
.windowbox {
background-color: @backtransparent;
border-radius: 20px;
box-shadow: 5px 5px 4px 4px #202020;
margin-bottom: 1em;
margin-right: 20px;
margin-top: 0.4em;
margin-left: 20px;
min-height: 30px;
border-color: @base08;
border-width: 3px;
}
button {
background-color: @basetransparent;
background-image: none;
border-color: @basetransparent;
}
.workspaces button {
} }
.workspaces { .workspaces {
background-color: blue; margin-left: 1em;
border-color: purple;
} }
.client-title {
color: @base0D;
}
/* button { */
/* background-color: @base00; */
/* border-color: purple; */
/* } */
button:active { button:active {
background-color: @theme_selected_bg_color; background-color: @basetransparent;
color: @base0D;
} }
button:hover { button:hover {
border-bottom: 3px solid @theme_fg_color; background-color: @basetransparent;
color: @base0D;
} }
.workspaces button.focused { .workspaces button.focused {
background-color: @theme_selected_bg_color; background-color: @basetransparent;
color: @theme_selected_fg_color; color: @base0D;
}
.window-title-box {
margin-left: 1em;
}
.systemtray {
}
.clock {
color: @base0B;
}
.battery_dial_charging {
min-width: 30px;
min-height: 30px;
font-size: 4px;
margin-top: 1em;
margin: 1em;
background-color: @basetransparent;
color: @base0B;
}
.battery_dial_charged {
min-width: 30px;
min-height: 30px;
font-size: 4px;
margin-top: 1em;
margin: 1em;
background-color: @basetransparent;
color: @base0B;
}
.battery_dial {
min-width: 30px;
min-height: 30px;
font-size: 4px;
margin-top: 1em;
margin: 1em;
background-color: @basetransparent;
color: @base0A;
}
.battery_icon {
font-size: 12px;
}
window.notification-popups box.notifications {
padding: .5em;
}
.notification {
min-width: 100px;
border-radius: 20px;
padding: 1em;
margin: 1em;
background-color: @backtransparent;
}
.notif-icon {
min-width: 38px;
min-height: 38px;
margin-right: 1em;
}
.notif-icon image {
font-size: 28px;
/* to center the icon */
margin: 5px;
color: @base0C;
}
.notif-icon box {
min-width: 38px;
min-height: 38px;
border-radius: 7px;
}
.notif-title {
color: @base0C;
font-size: 1.2em;
margin: 5px;
}
.notif-body {
color: @base0C;
}
.notif-actions .notif-action-button {
margin: 0 .4em;
margin-top: .8em;
}
.notif-actions .notif-action-button:first-child {
margin-left: 0;
}
.notif-actions .notif-action-button:last-child {
margin-right: 0;
} }

View file

@ -1,5 +1,25 @@
{ {
"nodes": { "nodes": {
"ags": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1718921313,
"narHash": "sha256-TFJah1RW5qnYW7kajjAFPAS5j/0q0R3vz9zPjrRA0Mc=",
"owner": "Aylur",
"repo": "ags",
"rev": "646d5ad073ff7f8b1d50cfbd40f5b8a250fcd59d",
"type": "github"
},
"original": {
"owner": "Aylur",
"repo": "ags",
"type": "github"
}
},
"base16": { "base16": {
"inputs": { "inputs": {
"fromYaml": "fromYaml" "fromYaml": "fromYaml"
@ -612,6 +632,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"ags": "ags",
"emacs": "emacs", "emacs": "emacs",
"eww": "eww", "eww": "eww",
"home-manager": "home-manager", "home-manager": "home-manager",

View file

@ -27,10 +27,10 @@
url = "github:elkowar/eww"; url = "github:elkowar/eww";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# ags = { ags = {
# url = "github:Aylur/ags"; url = "github:Aylur/ags";
# inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
# }; };
emacs = { emacs = {
url = "github:nix-community/emacs-overlay"; url = "github:nix-community/emacs-overlay";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@ -67,7 +67,7 @@
nix-bitcoin, nix-bitcoin,
libre-presenter, libre-presenter,
eww, eww,
# ags, ags,
stylix, stylix,
rust-overlay, ... }: rust-overlay, ... }:
let let
@ -107,7 +107,7 @@
{ {
home-manager.useGlobalPkgs = true; home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true; home-manager.useUserPackages = true;
# home-manager.extraSpecialArgs = { inherit ags; }; home-manager.extraSpecialArgs = { inherit ags; };
home-manager.users.chris = import ./home/home.nix; home-manager.users.chris = import ./home/home.nix;
} }
]; ];
@ -124,7 +124,7 @@
{ {
home-manager.useGlobalPkgs = true; home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true; home-manager.useUserPackages = true;
# home-manager.extraSpecialArgs = { inherit ags; }; home-manager.extraSpecialArgs = { inherit ags; };
home-manager.users.chris = import ./home/home.nix; home-manager.users.chris = import ./home/home.nix;
} }
]; ];

View file

@ -1,4 +1,4 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ags, ... }:
let let
laptop = builtins.readFile "/etc/hostname" == "syl\n"; laptop = builtins.readFile "/etc/hostname" == "syl\n";
@ -6,7 +6,7 @@ in
{ {
imports = [ imports = [
./modules/hyprland.nix ./modules/hyprland.nix
# ags.homeManagerModules.default ags.homeManagerModules.default
]; ];
# Home Manager needs a bit of information about you and the # Home Manager needs a bit of information about you and the
# paths it should manage. # paths it should manage.
@ -729,14 +729,14 @@ tooltip label {
source = ../.config/fish/functions; source = ../.config/fish/functions;
}; };
# programs.ags = { programs.ags = {
# enable = true; enable = true;
# extraPackages = with pkgs; [ extraPackages = with pkgs; [
# gtksourceview gtksourceview
# webkitgtk webkitgtk
# accountsservice accountsservice
# ]; ];
# }; };
programs.fish = { programs.fish = {
enable = true; enable = true;

View file

@ -327,10 +327,11 @@ in
exec-once = [ exec-once = [
"kwalletd5" "kwalletd5"
"eww daemon" # "eww daemon"
"swww-daemon --format xrgb" "swww-daemon --format xrgb"
"eww open ${if laptop then "bar0" else "bar1"}" # "eww open ${if laptop then "bar0" else "bar1"}"
"dunst" # "dunst"
"ags"
"rbw-agent" "rbw-agent"
"/home/chris/bin/startup.sh" "/home/chris/bin/startup.sh"
"hyprctl dispatch --batch 'splitratio 1; splitration -0.35'" "hyprctl dispatch --batch 'splitratio 1; splitration -0.35'"

View file

@ -60,6 +60,11 @@
ts ts
du-dust du-dust
sbcl sbcl
clojure
clojure-lsp
clj-kondo
leiningen
]; ];
} }