feat: ags

This commit is contained in:
Sweetbread 2024-12-27 15:42:51 +03:00
parent 21fe030dc7
commit 8ea1207bb9
21 changed files with 357 additions and 1214 deletions

224
flake.lock generated
View File

@ -25,11 +25,11 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1735172916,
"narHash": "sha256-gaX0dray5HOMygc/MtYJHQCqh6ZBkLsy1A2vVibFLZg=",
"lastModified": 1735346534,
"narHash": "sha256-geDO7T1mbCr9dhc7JWPor+zsCBMwbJL2ADq9I3r+B9g=",
"owner": "Aylur",
"repo": "ags",
"rev": "46aad56c4eb33cd83cd32b4757b96731bebc81a7",
"rev": "bb963edccb61be0f40cb8767ab2bf45324fff3f3",
"type": "github"
},
"original": {
@ -38,25 +38,6 @@
"type": "github"
}
},
"ags_2": {
"inputs": {
"astal": "astal_2",
"nixpkgs": "nixpkgs_5"
},
"locked": {
"lastModified": 1734091628,
"narHash": "sha256-8O3i8zESjHVsGzyXb8gEpLztvANq3Ot5bwo60YKJc7k=",
"owner": "aylur",
"repo": "ags",
"rev": "27cd93147aba09142fa585fd16f13c56268b696c",
"type": "github"
},
"original": {
"owner": "aylur",
"repo": "ags",
"type": "github"
}
},
"aquamarine": {
"inputs": {
"hyprutils": [
@ -98,51 +79,11 @@
]
},
"locked": {
"lastModified": 1733520119,
"narHash": "sha256-6K07ZJTnFu1xASBCMtVc9cFTbBEauwSc7gGBmjLkLSk=",
"lastModified": 1735172721,
"narHash": "sha256-rtEAwGsHSppnkR3Qg3eRJ6Xh/F84IY9CrBBLzYabalY=",
"owner": "aylur",
"repo": "astal",
"rev": "4c19d8d06fa25cc6389f37abe8839b4d8be5c0d6",
"type": "github"
},
"original": {
"owner": "aylur",
"repo": "astal",
"type": "github"
}
},
"astal_2": {
"inputs": {
"nixpkgs": [
"hyprpanel",
"ags",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733520119,
"narHash": "sha256-6K07ZJTnFu1xASBCMtVc9cFTbBEauwSc7gGBmjLkLSk=",
"owner": "aylur",
"repo": "astal",
"rev": "4c19d8d06fa25cc6389f37abe8839b4d8be5c0d6",
"type": "github"
},
"original": {
"owner": "aylur",
"repo": "astal",
"type": "github"
}
},
"astal_3": {
"inputs": {
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1734814417,
"narHash": "sha256-R+tLGIxlaqsOmV52TdXHP0u33q5PdJ77gtiUPK5BbMg=",
"owner": "aylur",
"repo": "astal",
"rev": "3468763d51d389c67ec7b1a390ffa8a5328bddb6",
"rev": "6c84b64efc736e039a8a10774a4a1bf772c37aa2",
"type": "github"
},
"original": {
@ -619,26 +560,6 @@
"type": "github"
}
},
"hyprpanel": {
"inputs": {
"ags": "ags_2",
"astal": "astal_3",
"nixpkgs": "nixpkgs_7"
},
"locked": {
"lastModified": 1735207839,
"narHash": "sha256-4PlQB9nan29e5Kl0U9Eyl5mKz8GmR1NEChBmfBhWOp0=",
"owner": "Jas-SinghFSU",
"repo": "HyprPanel",
"rev": "0c9b72708e02388c35e1524929982412165189c7",
"type": "github"
},
"original": {
"owner": "Jas-SinghFSU",
"repo": "HyprPanel",
"type": "github"
}
},
"hyprutils": {
"inputs": {
"nixpkgs": [
@ -749,45 +670,13 @@
"type": "github"
}
},
"nixpkgs_10": {
"locked": {
"lastModified": 1732238832,
"narHash": "sha256-sQxuJm8rHY20xq6Ah+GwIUkF95tWjGRd1X8xF+Pkk38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8edf06bea5bcbee082df1b7369ff973b91618b8d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_11": {
"locked": {
"lastModified": 1715534503,
"narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1733581040,
"narHash": "sha256-Qn3nPMSopRQJgmvHzVqPcE3I03zJyl8cSbgnnltfFDY=",
"lastModified": 1734649271,
"narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "22c3f2cf41a0e70184334a958e6b124fb0ce3e01",
"rev": "d70bd19e0a38ad4790d3913bf08fcbfc9eeca507",
"type": "github"
},
"original": {
@ -831,11 +720,11 @@
},
"nixpkgs_5": {
"locked": {
"lastModified": 1733581040,
"narHash": "sha256-Qn3nPMSopRQJgmvHzVqPcE3I03zJyl8cSbgnnltfFDY=",
"lastModified": 1734649271,
"narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "22c3f2cf41a0e70184334a958e6b124fb0ce3e01",
"rev": "d70bd19e0a38ad4790d3913bf08fcbfc9eeca507",
"type": "github"
},
"original": {
@ -846,54 +735,6 @@
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1734424634,
"narHash": "sha256-cHar1vqHOOyC7f1+tVycPoWTfKIaqkoe1Q6TnKzuti4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d3c42f187194c26d9f0309a8ecc469d6c878ce33",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1734649271,
"narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d70bd19e0a38ad4790d3913bf08fcbfc9eeca507",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_8": {
"locked": {
"lastModified": 1734649271,
"narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "d70bd19e0a38ad4790d3913bf08fcbfc9eeca507",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_9": {
"locked": {
"lastModified": 1731763621,
"narHash": "sha256-ddcX4lQL0X05AYkrkV2LMFgGdRvgap7Ho8kgon3iWZk=",
@ -909,6 +750,38 @@
"type": "github"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1732238832,
"narHash": "sha256-sQxuJm8rHY20xq6Ah+GwIUkF95tWjGRd1X8xF+Pkk38=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8edf06bea5bcbee082df1b7369ff973b91618b8d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_8": {
"locked": {
"lastModified": 1715534503,
"narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat_2",
@ -941,8 +814,7 @@
"home-manager": "home-manager",
"hyprland": "hyprland",
"hyprland-plugins": "hyprland-plugins",
"hyprpanel": "hyprpanel",
"nixpkgs": "nixpkgs_8",
"nixpkgs": "nixpkgs_5",
"nixpkgs-stable": "nixpkgs-stable_2",
"sops-nix": "sops-nix",
"stylix": "stylix",
@ -951,7 +823,7 @@
},
"sops-nix": {
"inputs": {
"nixpkgs": "nixpkgs_9"
"nixpkgs": "nixpkgs_6"
},
"locked": {
"lastModified": 1734546875,
@ -977,7 +849,7 @@
"flake-utils": "flake-utils",
"gnome-shell": "gnome-shell",
"home-manager": "home-manager_2",
"nixpkgs": "nixpkgs_10",
"nixpkgs": "nixpkgs_7",
"systems": "systems_2",
"tinted-foot": "tinted-foot",
"tinted-kitty": "tinted-kitty",
@ -1080,7 +952,7 @@
"tlock": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_11"
"nixpkgs": "nixpkgs_8"
},
"locked": {
"lastModified": 1716429453,

View File

@ -24,7 +24,6 @@
ayugram-desktop.url = "github:/ayugram-port/ayugram-desktop/release?submodules=1";
tlock.url = "git+https://github.com/eklairs/tlock?submodules=1";
hyprpanel.url = "github:Jas-SinghFSU/HyprPanel";
hyprland.url = "github:hyprwm/Hyprland";
hyprland-plugins = {
url = "github:hyprwm/hyprland-plugins";
@ -37,13 +36,12 @@
};
};
outputs = { self, nixpkgs, nixpkgs-stable, home-manager, ... }@inputs: let
system = "x86_64-linux";
pkgs = import nixpkgs {
inherit system;
overlays = [ inputs.hyprpanel.overlay ];
};
in {
outputs = { self, nixpkgs, nixpkgs-stable, home-manager, ... }@inputs:
let
system = "x86_64-linux";
in {
nixosConfigurations = {
Rias = nixpkgs.lib.nixosSystem {
specialArgs = {
@ -90,7 +88,7 @@
homeConfigurations = {
sweetbread = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
pkgs = nixpkgs.legacyPackages.${system};
extraSpecialArgs = {
inherit inputs;
pkgs-stable = import nixpkgs-stable {
@ -102,12 +100,11 @@
./home-manager/users/sweetbread/home.nix
inputs.sops-nix.homeManagerModules.sops
inputs.stylix.homeManagerModules.stylix
inputs.ags.homeManagerModules.default
];
};
chest = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
pkgs = nixpkgs.legacyPackages.${system};
extraSpecialArgs = {
inherit inputs;
pkgs-stable = import nixpkgs-stable {
@ -119,7 +116,6 @@
./home-manager/users/chest/home.nix
inputs.sops-nix.homeManagerModules.sops
inputs.stylix.homeManagerModules.stylix
inputs.ags.homeManagerModules.default
];
};
};

View File

@ -1,325 +1,20 @@
{ inputs, pkgs, config, ... }: {
# imports = [ inputs.ags.homeManagerModules.default ];
{ inputs, pkgs, ... }: {
imports = [ inputs.ags.homeManagerModules.default ];
programs.ags = {
enable = true;
# null or path, leave as null if you don't want hm to manage the config
configDir = null;
configDir = ./ags;
# additional packages to add to gjs's runtime
extraPackages = with pkgs; [
gtksourceview
webkitgtk
accountsservice
extraPackages = with inputs.ags.packages.${pkgs.system}; [
battery
mpris
hyprland
network
tray
wireplumber
];
};
xdg.configFile."ags/config.js".text = /*js*/ ''
const hyprland = await Service.import("hyprland")
const notifications = await Service.import("notifications")
const mpris = await Service.import("mpris")
const audio = await Service.import("audio")
const battery = await Service.import("battery")
const systemtray = await Service.import("systemtray")
const date = Variable("", {
poll: [1000, 'date "+%H:%M:%S %b %e."'],
})
// widgets can be only assigned as a child in one container
// so to make a reuseable widget, make it a function
// 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: hyprland.active.client.bind("title"),
})
}
function Clock() {
return Widget.Label({
class_name: "clock",
label: date.bind(),
})
}
// 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() {
const label = Utils.watch("default", mpris, "player-changed", () => {
if (mpris.players[0]) {
const { track_artists, track_title } = mpris.players[0]
return `''${track_artists.join(", ")} - ''${track_title}`
} else {
return "Nothing is playing"
}
})
print(Object.values(label))
if (label != "default") {
return Widget.Button({
class_name: "media",
on_primary_click: () => mpris.getPlayer("")?.playPause(),
on_scroll_up: () => mpris.getPlayer("")?.next(),
on_scroll_down: () => mpris.getPlayer("")?.previous(),
child: Widget.Label({ label }),
})
}
}
function Volume() {
const icons = {
101: "overamplified",
67: "high",
34: "medium",
1: "low",
0: "muted",
}
function getIcon() {
const icon = audio.speaker.is_muted ? 0 : [101, 67, 34, 1, 0].find(
threshold => threshold <= audio.speaker.volume * 100)
return `audio-volume-''${icons[icon]}-symbolic`
}
const icon = Widget.Icon({
icon: Utils.watch(getIcon(), audio.speaker, getIcon),
})
const slider = Widget.Slider({
hexpand: true,
draw_value: false,
on_change: ({ value }) => audio.speaker.volume = value,
setup: self => self.hook(audio.speaker, () => {
self.value = audio.speaker.volume || 0
}),
})
return Widget.Box({
class_name: "volume",
css: "min-width: 180px",
children: [icon, slider],
})
}
function BatteryLabel() {
const value = battery.bind("percent").as(p => p > 0 ? p / 100 : 0)
const icon = battery.bind("percent").as(p =>
`battery-level-''${Math.floor(p / 10) * 10}-symbolic`)
return Widget.Box({
class_name: "battery",
visible: battery.bind("available"),
children: [
Widget.Icon({ icon }),
Widget.LevelBar({
widthRequest: 140,
vpack: "center",
value,
}),
],
})
}
function SysTray() {
const items = systemtray.bind("items")
.as(items => items.map(item => Widget.Button({
child: Widget.Icon({ icon: item.bind("icon") }),
on_primary_click: (_, event) => item.activate(event),
on_secondary_click: (_, event) => item.openMenu(event),
tooltip_markup: item.bind("tooltip_markup"),
})))
return Widget.Box({
children: items,
})
}
// layout of the bar
function Left() {
return Widget.Box({
spacing: 8,
children: [
Workspaces(),
ClientTitle(),
],
})
}
function Center() {
return Widget.Box({
spacing: 8,
children: [
Media(),
Notification(),
],
})
}
function Right() {
return Widget.Box({
hpack: "end",
spacing: 8,
children: [
Volume(),
BatteryLabel(),
Clock(),
SysTray(),
],
})
}
function Bar(monitor = 0) {
return Widget.Window({
name: `bar-''${monitor}`, // name has to be unique
class_name: "bar",
monitor,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
child: Widget.CenterBox({
start_widget: Left(),
center_widget: Center(),
end_widget: Right(),
}),
})
}
App.config({
style: "./style.css",
windows: [
Bar(),
// you can call it, for each monitor
// Bar(0),
// Bar(1)
],
})
export { }
'';
xdg.configFile."ags/style.css".text = let
colors = config.lib.stylix.colors;
color = {
base = colors.base00;
mantle = colors.base01;
surface0 = colors.base02;
surface1 = colors.base03;
surface2 = colors.base04;
text = colors.base05;
rosewater = colors.base06;
lavander = colors.base07;
red = colors.base08;
peach = colors.base09;
yellow = colors.base0A;
green = colors.base0B;
teal = colors.base0C;
blue = colors.base0D;
mauve = colors.base0E;
flamingo = colors.base0F;
};
in /*css*/ ''
* {
border: none;
box-shadow: none;
text-shadow: none;
}
window.bar {
background-color: transparent;
color: #${color.text};
}
button {
/* size: 20px; */
background-color: transparent;
border-radius: 10px;
}
button:active {
background-color: /*#${color.surface0}*/ red;
}
label {
font-weight: bold;
}
.workspaces {
background-color: #${color.base};
border-radius: 10px;
}
.workspaces button {
color: #${color.surface2};
background: transparent;
padding: 5px;
}
.workspaces button:hover {
color: #${color.text};
}
.workspaces button.focused {
background-color: #${color.surface0};
color: #${color.text};
}
.client-title {
color: @theme_selected_bg_color;
}
.notification {
color: yellow;
}
levelbar block,
highlight {
min-height: 10px;
}
'';
# home.packages = [ pkgs.ags ];
wayland.windowManager.hyprland.settings.exec-once = [ "ags run" ];
}

View File

@ -1,18 +1,12 @@
# Simple Bar
# Simple Bar Example
setup
![simple-bar](https://github.com/user-attachments/assets/a306c864-56b7-44c4-8820-81f424f32b9b)
```bash
mkdir -p ~/.config/ags
git clone https://github.com/Aylur/ags.git /tmp/ags
cp -r /tmp/ags/example/simple-bar/* ~/.config/ags
A simple bar for Hyprland using
# optionally setup types
ags --init -c ~/.config/ags/config.js
```
running
```bash
ags -c ~/.config/ags/config.js &
```
- [Battery library](https://aylur.github.io/astal/guide/libraries/battery).
- [Hyprland library](https://aylur.github.io/astal/guide/libraries/hyprland).
- [Mpris library](https://aylur.github.io/astal/guide/libraries/mpris).
- [Network library](https://aylur.github.io/astal/guide/libraries/network).
- [Tray library](https://aylur.github.io/astal/guide/libraries/tray).
- [WirePlumber library](https://aylur.github.io/astal/guide/libraries/wireplumber).

View File

@ -0,0 +1,13 @@
import { App } from "astal/gtk3"
import style from "./style.scss"
import Bar from "./widget/Bar"
App.start({
css: style,
instanceName: "js",
requestHandler(request, res) {
print(request)
res("ok")
},
main: () => App.get_monitors().map(Bar),
})

View File

@ -1,219 +0,0 @@
const hyprland = await Service.import("hyprland")
const notifications = await Service.import("notifications")
const mpris = await Service.import("mpris")
const audio = await Service.import("audio")
const battery = await Service.import("battery")
const systemtray = await Service.import("systemtray")
const date = Variable("", {
poll: [1000, 'date "+%H:%M:%S %b %e."'],
})
// widgets can be only assigned as a child in one container
// so to make a reuseable widget, make it a function
// 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: hyprland.active.client.bind("title"),
})
}
function Clock() {
return Widget.Label({
class_name: "clock",
label: date.bind(),
})
}
// 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() {
const label = Utils.watch("", mpris, "player-changed", () => {
if (mpris.players[0]) {
const { track_artists, track_title } = mpris.players[0]
return `${track_artists.join(", ")} - ${track_title}`
} else {
return "Nothing is playing"
}
})
return Widget.Button({
class_name: "media",
on_primary_click: () => mpris.getPlayer("")?.playPause(),
on_scroll_up: () => mpris.getPlayer("")?.next(),
on_scroll_down: () => mpris.getPlayer("")?.previous(),
child: Widget.Label({ label }),
})
}
function Volume() {
const icons = {
101: "overamplified",
67: "high",
34: "medium",
1: "low",
0: "muted",
}
function getIcon() {
const icon = audio.speaker.is_muted ? 0 : [101, 67, 34, 1, 0].find(
threshold => threshold <= audio.speaker.volume * 100)
return `audio-volume-${icons[icon]}-symbolic`
}
const icon = Widget.Icon({
icon: Utils.watch(getIcon(), audio.speaker, getIcon),
})
const slider = Widget.Slider({
hexpand: true,
draw_value: false,
on_change: ({ value }) => audio.speaker.volume = value,
setup: self => self.hook(audio.speaker, () => {
self.value = audio.speaker.volume || 0
}),
})
return Widget.Box({
class_name: "volume",
css: "min-width: 180px",
children: [icon, slider],
})
}
function BatteryLabel() {
const value = battery.bind("percent").as(p => p > 0 ? p / 100 : 0)
const icon = battery.bind("percent").as(p =>
`battery-level-${Math.floor(p / 10) * 10}-symbolic`)
return Widget.Box({
class_name: "battery",
visible: battery.bind("available"),
children: [
Widget.Icon({ icon }),
Widget.LevelBar({
widthRequest: 140,
vpack: "center",
value,
}),
],
})
}
function SysTray() {
const items = systemtray.bind("items")
.as(items => items.map(item => Widget.Button({
child: Widget.Icon({ icon: item.bind("icon") }),
on_primary_click: (_, event) => item.activate(event),
on_secondary_click: (_, event) => item.openMenu(event),
tooltip_markup: item.bind("tooltip_markup"),
})))
return Widget.Box({
children: items,
})
}
// layout of the bar
function Left() {
return Widget.Box({
spacing: 8,
children: [
Workspaces(),
ClientTitle(),
],
})
}
function Center() {
return Widget.Box({
spacing: 8,
children: [
Media(),
Notification(),
],
})
}
function Right() {
return Widget.Box({
hpack: "end",
spacing: 8,
children: [
Volume(),
BatteryLabel(),
Clock(),
SysTray(),
],
})
}
function Bar(monitor = 0) {
return Widget.Window({
name: `bar-${monitor}`, // name has to be unique
class_name: "bar",
monitor,
anchor: ["top", "left", "right"],
exclusivity: "exclusive",
child: Widget.CenterBox({
start_widget: Left(),
center_widget: Center(),
end_widget: Right(),
}),
})
}
App.config({
style: "./style.css",
windows: [
Bar(),
// you can call it, for each monitor
// Bar(0),
// Bar(1)
],
})
export { }

View File

@ -0,0 +1,106 @@
@use "sass:color";
$bg: #212223;
$fg: #f1f1f1;
$accent: #378DF7;
$radius: 7px;
window.Bar {
border: none;
box-shadow: none;
background-color: $bg;
color: $fg;
font-size: 1.1em;
font-weight: bold;
label {
margin: 0 8px;
}
.Workspaces {
button {
all: unset;
background-color: transparent;
&:hover label {
background-color: color.adjust($fg, $alpha: -0.84);
border-color: color.adjust($accent, $alpha: -0.8);
}
&:active label {
background-color: color.adjust($fg, $alpha: -0.8)
}
}
label {
transition: 200ms;
padding: 0 8px;
margin: 2px;
border-radius: $radius;
border: 1pt solid transparent;
}
.focused label {
color: $accent;
border-color: $accent;
}
}
.SysTray {
margin-right: 8px;
button {
padding: 0 4px;
}
}
.FocusedClient {
color: $accent;
}
.Media .Cover {
min-height: 1.2em;
min-width: 1.2em;
border-radius: $radius;
background-position: center;
background-size: contain;
}
.Battery label {
padding-left: 0;
margin-left: 0;
}
.AudioSlider {
* {
all: unset;
}
icon {
margin-right: .6em;
}
& {
margin: 0 1em;
}
trough {
background-color: color.adjust($fg, $alpha: -0.8);
border-radius: $radius;
}
highlight {
background-color: $accent;
min-height: .8em;
border-radius: $radius;
}
slider {
background-color: $fg;
border-radius: $radius;
min-height: 1em;
min-width: 1em;
margin: -.2em;
}
}
}

View File

@ -0,0 +1,37 @@
import { App } from "astal/gtk3"
import { Astal, Gtk, Gdk } from "astal/gtk3"
import Time from "./elements/Time"
import Wifi from "./elements/Wifi"
import Audio from "./elements/Audio"
import Media from "./elements/Media"
import SysTray from "./elements/SysTray"
import Workspaces from "./elements/Workspaces"
import BatteryLevel from "./elements/Battery"
export default function Bar(monitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
return <window
className="Bar"
gdkmonitor={monitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}>
<centerbox>
<box hexpand halign={Gtk.Align.START}>
<Workspaces />
</box>
<box>
<Media />
</box>
<box hexpand halign={Gtk.Align.END} >
<SysTray />
<Wifi />
<Audio />
<BatteryLevel />
<Time />
</box>
</centerbox>
</window>
}

View File

@ -0,0 +1,15 @@
import { bind } from "astal"
import Wp from "gi://AstalWp"
export default function Audio() {
const speaker = Wp.get_default()?.audio.defaultSpeaker!
return <box className="AudioSlider" css="min-width: 140px">
<icon icon={bind(speaker, "volumeIcon")} />
<slider
hexpand
onDragged={({ value }) => speaker.volume = value}
value={bind(speaker, "volume")}
/>
</box>
}

View File

@ -0,0 +1,14 @@
import { bind } from "astal"
import Battery from "gi://AstalBattery"
export default function BatteryLevel() {
const bat = Battery.get_default()
return <box className="Battery"
visible={bind(bat, "isPresent")}>
<icon icon={bind(bat, "batteryIconName")} />
<label label={bind(bat, "percentage").as(p =>
`${Math.floor(p * 100)} %`
)} />
</box>
}

View File

@ -0,0 +1,28 @@
import { bind } from "astal"
import { Gtk } from "astal/gtk3"
import Mpris from "gi://AstalMpris"
export default function Media() {
const mpris = Mpris.get_default()
return <box className="Media">
{bind(mpris, "players").as(ps => ps[0] ? (
<box>
<box
className="Cover"
valign={Gtk.Align.CENTER}
css={bind(ps[0], "coverArt").as(cover =>
`background-image: url('${cover}');`
)}
/>
<label
label={bind(ps[0], "title").as(() =>
`${ps[0].title} - ${ps[0].artist}`
)}
/>
</box>
) : (
"Nothing Playing"
))}
</box>
}

View File

@ -0,0 +1,18 @@
import { bind } from "astal"
import Tray from "gi://AstalTray"
export default function SysTray() {
const tray = Tray.get_default()
return <box className="SysTray">
{bind(tray, "items").as(items => items.map(item => (
<menubutton
tooltipMarkup={bind(item, "tooltipMarkup")}
usePopover={false}
actionGroup={bind(item, "action-group").as(ag => ["dbusmenu", ag])}
menuModel={bind(item, "menu-model")}>
<icon gicon={bind(item, "gicon")} />
</menubutton>
)))}
</box>
}

View File

@ -0,0 +1,12 @@
import { Variable, GLib } from "astal"
export default function Time({ format = "%H:%M - %A %e." }) {
const time = Variable<string>("").poll(1000, () =>
GLib.DateTime.new_now_local().format(format)!)
return <label
className="Time"
onDestroy={() => time.drop()}
label={time()}
/>
}

View File

@ -0,0 +1,17 @@
import { bind } from "astal"
import Network from "gi://AstalNetwork"
export default function Wifi() {
const network = Network.get_default()
const wifi = bind(network, "wifi")
return <box visible={wifi.as(Boolean)}>
{wifi.as(wifi => wifi && (
<icon
tooltipText={bind(wifi, "ssid").as(String)}
className="Wifi"
icon={bind(wifi, "iconName")}
/>
))}
</box>
}

View File

@ -0,0 +1,21 @@
import { bind } from "astal"
import Hyprland from "gi://AstalHyprland"
export default function Workspaces() {
const hypr = Hyprland.get_default()
return <box className="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
className={bind(hypr, "focusedWorkspace").as(fw =>
ws === fw ? "focused" : "")}
onClicked={() => ws.focus()}>
{ws.id}
</button>
))
)}
</box>
}

View File

@ -134,7 +134,6 @@
"systemctl --user start plasma-polkit-agent"
"swww init"
"${lib.getExe wallpaper_changer}"
"${lib.getExe pkgs.hyprpanel}"
"${clipsync}"
"clipse -listen"
];

View File

@ -1,455 +0,0 @@
{ pkgs, config, active_color }: {
home.packages = [ pkgs.pulsemixer ];
wayland.windowManager.hyprland.settings.windowrule = [
"float, ^(pulsemixer)"
"float, ^(nmtui)"
];
xdg.configFile."waybar/scripts/wttr.py".source = pkgs.fetchurl {
name = "waybar-wttr.py";
url = "https://gist.githubusercontent.com/bjesus/f8db49e1434433f78e5200dc403d58a3/raw/47f9ffd573dc8e8edce0ea6708601b8e685a70ab/waybar-wttr.py";
sha256 = "15j2cqg405q37wrrlm70mhp7rx6xnrn92rfm1ix6g3nl98ksh45g";
};
programs.waybar =
let
colors = config.lib.stylix.colors;
in {
enable = true;
settings = {
mainBar = {
layer = "top";
position = "top";
margin = "9 13 -10 18";
modules-left = ["hyprland/workspaces" "hyprland/language" "keyboard-state" "hyprland/submap"];
modules-center = ["mpris" "wlr/taskbar"];
modules-right = ["group/system" "battery" "pulseaudio" "clock" "tray"];
"hyprland/workspaces" = {
disable-scroll = true;
};
"wlr/taskbar" = {
on-click = "activate";
on-click-middle = "close";
on-click-right = "minimize";
};
"group/system" = {
orientation = "inherit";
drawer = {
transition-duration = 500;
transition-left-to-right = false;
};
modules = [
"network"
"custom/mem"
"cpu"
"temperature"
"backlight"
];
};
"hyprland/language" = {
format-en = "US";
format-ru = "RU";
min-length = 5;
tooltip = false;
};
"keyboard-state" = {
capslock = true;
format = "{icon}";
format-icons = {
locked = "CUPS";
unlocked = "";
};
};
"clock" = {
tooltip = false;
format = "{:%a, %d %b %R}";
};
"custom/weather" = {
format = "{}";
tooltip = true;
interval = 1800;
exec = "python3 $HOME/.config/waybar/scripts/wttr.py";
return-type = "json";
};
"pulseaudio" = {
# scroll-step = 1; # %, can be a float
reverse-scrolling = 1;
format = "{volume}% {icon} {format_source}";
format-bluetooth = "{volume}% {icon} {format_source}";
format-bluetooth-muted = " {icon} {format_source}";
format-muted = " {format_source}";
format-source = "{volume}% ";
format-source-muted = "";
format-icons = {
headphone = "";
hands-free = "";
headset = "";
phone = "";
portable = "";
car = "";
default = ["" "" ""];
};
on-click = "kitty --class pulsemixer -e pulsemixer";
min-length = 13;
};
"custom/mem" = {
format = "{} ";
interval = 3;
exec = "free -h | awk '/Mem:/{printf $3}'";
tooltip = false;
};
"cpu" = {
interval = 2;
format = "{usage}% ";
min-length = 6;
};
"temperature" = {
# thermal-zone = 2;
# hwmon-path = "/sys/class/hwmon/hwmon2/temp1_input";
critical-threshold = 80;
# format-critical = "{temperatureC}°C {icon}";
format = "{temperatureC}°C {icon}";
format-icons = ["" "" "" "" ""];
tooltip = false;
};
"network" = {
format = "{ifname}";
format-wifi = "{essid} ({signalStrength}%) ";
format-ethernet = "{ifname} ";
format-disconnected = "";
tooltip-format = "{ifname}";
tooltip-format-wifi = "{essid} ({signalStrength}%) ";
tooltip-format-ethernet = "{ifname} ";
tooltip-format-disconnected = "Disconnected";
max-length = 50;
on-click = "kitty --class nmtui -e sh -c nmtui";
};
"backlight" = {
device = "intel_backlight";
format = "{percent}% {icon}";
format-icons = [""];
min-length = 7;
};
battery = {
states = {
warning = 30;
critical = 15;
};
format = "{capacity}%";
format-charging = "{capacity}% {time}";
format-plugged = "{capacity}%";
format-alt = "{time}";
format-time = "{H}:{m}";
};
mpris = {
format = "{title}";
format-len = "20";
};
tray = {
icon-size = 16;
spacing = 0;
};
};
};
style = /*css*/
''
* {
border: none;
border-radius: 0;
/* `otf-font-awesome` is required to be installed for icons */
font-family: JetBrains Mono;
font-weight: bold;
min-height: 20px;
}
window#waybar {
background: transparent;
}
window#waybar.hidden {
opacity: 0.2;
}
#workspaces {
margin-right: 8px;
border-radius: 10px;
transition: none;
background: #${colors.base00};
}
#workspaces button {
transition: none;
color: #${colors.base04};
background: transparent;
padding: 5px;
font-size: 18px;
}
#workspaces button.persistent {
font-size: 12px;
}
/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
#workspaces button:hover {
transition: none;
box-shadow: inherit;
text-shadow: inherit;
border-radius: inherit;
color: #${colors.base05};
}
#workspaces button.active {
background: #${colors.base02};
color: #${colors.base05};
border-radius: inherit;
}
#taskbar {
border-radius: 10px;
color: #${colors.base05};
background: #${colors.base00};
}
#taskbar button {
transition: none;
background: transparent;
border-radius: 10px;
padding: 8px;
margin: 0px;
}
#taskbar button:hover {
transition: none;
box-shadow: inherit;
text-shadow: inherit;
border-radius: inherit;
background: #${colors.base02};
}
#taskbar button.active {
background: ${active_color};
}
#language {
padding: 8px 0px 8px 8px;
border-radius: 10px 0px 0px 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#keyboard-state {
margin-right: 8px;
padding: 8px 8px 8px 0px;
border-radius: 0px 10px 10px 0px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#custom-pacman {
padding-left: 16px;
padding-right: 8px;
border-radius: 10px 0px 0px 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#custom-mail {
margin-right: 8px;
padding-right: 16px;
border-radius: 0px 10px 10px 0px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#submap {
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#clock {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#network {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#network.disconnected {
color: #${colors.base00};
background-color: #${colors.base08};
}
#custom-weather {
padding-right: 16px;
border-radius: 0px 10px 10px 0px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#pulseaudio {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#pulseaudio.muted {
background-color: #${colors.base08};
color: #${colors.base01};
}
#custom-mem {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#cpu {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#temperature {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#temperature.critical {
background-color: #${colors.base08};
}
#backlight {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#battery {
margin-right: 8px;
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#battery.charging {
color: #${colors.base05};
background-color: #${colors.base0B};
}
#battery.warning:not(.charging) {
background-color: #${colors.base09};
color: black;
}
#battery.critical:not(.charging) {
background-color: #${colors.base08};
color: #${colors.base05};
animation-name: blink;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#tray {
padding-left: 16px;
padding-right: 16px;
border-radius: 10px;
transition: none;
color: #${colors.base05};
background: #${colors.base00};
}
#mpris{
background: #${colors.base00};
border-radius: 10px;
color: #${colors.base05};
padding: 0px 8px;
margin: 0px 8px;
}
#mpris.playing {
background-color: #${colors.base0B};
color: #${colors.base01};
}
#mpris.paused {
background-color: #${colors.base0A};
color: #${colors.base01};
}
@keyframes blink {
to {
background-color: #${colors.base05};
color: #${colors.base00};
}
}
'';
};
}

View File

@ -8,7 +8,6 @@
./modules/git.nix
./modules/hyprland.nix
./modules/style.nix
./modules/waybar.nix
];
nixpkgs.config.allowUnfree = true;

View File

@ -1,9 +0,0 @@
{ pkgs, config, ... }: {
imports = [(
import ../../../patterns/waybar.nix {
inherit pkgs;
inherit config;
active_color = "#d197d9";
}
)];
}

View File

@ -11,7 +11,6 @@
./modules/git.nix
./modules/hyprland.nix
./modules/style.nix
./modules/waybar.nix
];
nixpkgs.config.allowUnfree = true;

View File

@ -1,9 +0,0 @@
{ pkgs, config, ... }: {
imports = [(
import ../../../patterns/waybar.nix {
inherit pkgs;
inherit config;
active_color = "#${config.lib.stylix.colors.base0B}";
}
)];
}