home/natto/ags: init
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
|||||||
*~
|
*~
|
||||||
*.html
|
*.html
|
||||||
result
|
result
|
||||||
|
.sass-cache
|
||||||
|
97
flake.lock
generated
97
flake.lock
generated
@@ -21,6 +21,24 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ags": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1715703984,
|
||||||
|
"narHash": "sha256-0BZkMui6aCqswMCouvp0G90tAxDOxVnxTvG6TDZsDaI=",
|
||||||
|
"owner": "Aylur",
|
||||||
|
"repo": "ags",
|
||||||
|
"rev": "11150225e62462bcd431d1e55185e810190a730a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "Aylur",
|
||||||
|
"repo": "ags",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"blobs": {
|
"blobs": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -41,7 +59,7 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
"nixpkgs": "nixpkgs_3",
|
"nixpkgs": "nixpkgs_4",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -83,7 +101,7 @@
|
|||||||
"emacs-overlay": {
|
"emacs-overlay": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_3",
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -103,7 +121,7 @@
|
|||||||
"filehost": {
|
"filehost": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"cargo2nix": "cargo2nix",
|
"cargo2nix": "cargo2nix",
|
||||||
"nixpkgs": "nixpkgs_4",
|
"nixpkgs": "nixpkgs_5",
|
||||||
"rust-overlay": "rust-overlay_2",
|
"rust-overlay": "rust-overlay_2",
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
},
|
},
|
||||||
@@ -430,7 +448,7 @@
|
|||||||
"hyprcursor": "hyprcursor",
|
"hyprcursor": "hyprcursor",
|
||||||
"hyprlang": "hyprlang",
|
"hyprlang": "hyprlang",
|
||||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||||
"nixpkgs": "nixpkgs_6",
|
"nixpkgs": "nixpkgs_7",
|
||||||
"systems": "systems_3",
|
"systems": "systems_3",
|
||||||
"xdph": "xdph"
|
"xdph": "xdph"
|
||||||
},
|
},
|
||||||
@@ -452,7 +470,7 @@
|
|||||||
},
|
},
|
||||||
"hyprland-contrib": {
|
"hyprland-contrib": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs_7"
|
"nixpkgs": "nixpkgs_8"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716228712,
|
"lastModified": 1716228712,
|
||||||
@@ -608,7 +626,7 @@
|
|||||||
"nix-gaming": {
|
"nix-gaming": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts_2",
|
||||||
"nixpkgs": "nixpkgs_8"
|
"nixpkgs": "nixpkgs_9"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716686274,
|
"lastModified": 1716686274,
|
||||||
@@ -681,6 +699,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_10": {
|
"nixpkgs_10": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1716715802,
|
||||||
|
"narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_11": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716588411,
|
"lastModified": 1716588411,
|
||||||
"narHash": "sha256-CdAZ3o459+1mAgILcdJfMBQAwUXupVe2cVTknvxs5kQ=",
|
"narHash": "sha256-CdAZ3o459+1mAgILcdJfMBQAwUXupVe2cVTknvxs5kQ=",
|
||||||
@@ -697,6 +731,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1708475490,
|
||||||
|
"narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "0e74ca98a74bc7270d28838369593635a5db3260",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716509168,
|
"lastModified": 1716509168,
|
||||||
"narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
|
"narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
|
||||||
@@ -712,7 +762,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1654275867,
|
"lastModified": 1654275867,
|
||||||
"narHash": "sha256-pt14ZE4jVPGvfB2NynGsl34pgXfOqum5YJNpDK4+b9E=",
|
"narHash": "sha256-pt14ZE4jVPGvfB2NynGsl34pgXfOqum5YJNpDK4+b9E=",
|
||||||
@@ -728,7 +778,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_5": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667085444,
|
"lastModified": 1667085444,
|
||||||
"narHash": "sha256-1SAlbifAAb+u8n52DUk6mB5oWv95o0qwRMHOMH3bS5g=",
|
"narHash": "sha256-1SAlbifAAb+u8n52DUk6mB5oWv95o0qwRMHOMH3bS5g=",
|
||||||
@@ -744,7 +794,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_5": {
|
"nixpkgs_6": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1665296151,
|
"lastModified": 1665296151,
|
||||||
"narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=",
|
"narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=",
|
||||||
@@ -760,7 +810,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_6": {
|
"nixpkgs_7": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716330097,
|
"lastModified": 1716330097,
|
||||||
"narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=",
|
"narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=",
|
||||||
@@ -776,7 +826,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_7": {
|
"nixpkgs_8": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1712163089,
|
"lastModified": 1712163089,
|
||||||
"narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=",
|
"narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=",
|
||||||
@@ -792,7 +842,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_8": {
|
"nixpkgs_9": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1716619601,
|
"lastModified": 1716619601,
|
||||||
"narHash": "sha256-9dUxZf8MOqJH3vjbhrz7LH4qTcnRsPSBU1Q50T7q/X8=",
|
"narHash": "sha256-9dUxZf8MOqJH3vjbhrz7LH4qTcnRsPSBU1Q50T7q/X8=",
|
||||||
@@ -808,29 +858,13 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_9": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1716715802,
|
|
||||||
"narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=",
|
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nixos",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nvim-overlay": {
|
"nvim-overlay": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_3",
|
"flake-compat": "flake-compat_3",
|
||||||
"flake-parts": "flake-parts_3",
|
"flake-parts": "flake-parts_3",
|
||||||
"hercules-ci-effects": "hercules-ci-effects",
|
"hercules-ci-effects": "hercules-ci-effects",
|
||||||
"neovim-src": "neovim-src",
|
"neovim-src": "neovim-src",
|
||||||
"nixpkgs": "nixpkgs_10",
|
"nixpkgs": "nixpkgs_11",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -877,6 +911,7 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"agenix": "agenix",
|
"agenix": "agenix",
|
||||||
|
"ags": "ags",
|
||||||
"emacs-overlay": "emacs-overlay",
|
"emacs-overlay": "emacs-overlay",
|
||||||
"filehost": "filehost",
|
"filehost": "filehost",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
@@ -886,7 +921,7 @@
|
|||||||
"mailserver": "mailserver",
|
"mailserver": "mailserver",
|
||||||
"nbfc": "nbfc",
|
"nbfc": "nbfc",
|
||||||
"nix-gaming": "nix-gaming",
|
"nix-gaming": "nix-gaming",
|
||||||
"nixpkgs": "nixpkgs_9",
|
"nixpkgs": "nixpkgs_10",
|
||||||
"nvim-overlay": "nvim-overlay",
|
"nvim-overlay": "nvim-overlay",
|
||||||
"stable": "stable"
|
"stable": "stable"
|
||||||
}
|
}
|
||||||
@@ -921,7 +956,7 @@
|
|||||||
"rust-overlay_2": {
|
"rust-overlay_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_3",
|
"flake-utils": "flake-utils_3",
|
||||||
"nixpkgs": "nixpkgs_5"
|
"nixpkgs": "nixpkgs_6"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667011705,
|
"lastModified": 1667011705,
|
||||||
|
15
flake.nix
15
flake.nix
@@ -3,38 +3,51 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable;
|
nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable;
|
||||||
|
|
||||||
stable.url = github:nixos/nixpkgs/release-23.11;
|
stable.url = github:nixos/nixpkgs/release-23.11;
|
||||||
|
|
||||||
flake-parts.url = github:hercules-ci/flake-parts;
|
flake-parts.url = github:hercules-ci/flake-parts;
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = github:nix-community/home-manager;
|
url = github:nix-community/home-manager;
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
mailserver = {
|
mailserver = {
|
||||||
url = gitlab:simple-nixos-mailserver/nixos-mailserver;
|
url = gitlab:simple-nixos-mailserver/nixos-mailserver;
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
filehost = {
|
filehost = {
|
||||||
url = github:natto1784/simpler-filehost;
|
url = github:natto1784/simpler-filehost;
|
||||||
# inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
nix-gaming.url = github:fufexan/nix-gaming;
|
nix-gaming.url = github:fufexan/nix-gaming;
|
||||||
|
|
||||||
nbfc = {
|
nbfc = {
|
||||||
url = github:nbfc-linux/nbfc-linux;
|
url = github:nbfc-linux/nbfc-linux;
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
emacs-overlay.url = github:nix-community/emacs-overlay;
|
emacs-overlay.url = github:nix-community/emacs-overlay;
|
||||||
|
|
||||||
nvim-overlay = {
|
nvim-overlay = {
|
||||||
url = github:nix-community/neovim-nightly-overlay;
|
url = github:nix-community/neovim-nightly-overlay;
|
||||||
};
|
};
|
||||||
|
|
||||||
hyprland = {
|
hyprland = {
|
||||||
type = "git";
|
type = "git";
|
||||||
url = "https://github.com/hyprwm/Hyprland";
|
url = "https://github.com/hyprwm/Hyprland";
|
||||||
submodules = true;
|
submodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
hyprland-contrib = {
|
hyprland-contrib = {
|
||||||
url = github:hyprwm/contrib;
|
url = github:hyprwm/contrib;
|
||||||
};
|
};
|
||||||
|
|
||||||
agenix.url = github:ryantm/agenix;
|
agenix.url = github:ryantm/agenix;
|
||||||
|
|
||||||
|
ags.url = github:Aylur/ags;
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs@{ self, ... }:
|
outputs = inputs@{ self, ... }:
|
||||||
|
@@ -23,8 +23,8 @@ in
|
|||||||
nattoModules = [
|
nattoModules = [
|
||||||
./natto
|
./natto
|
||||||
./common/laptop.nix
|
./common/laptop.nix
|
||||||
inputs.hyprland.homeManagerModules.default
|
|
||||||
inputs.agenix.homeManagerModules.default
|
inputs.agenix.homeManagerModules.default
|
||||||
|
inputs.hyprland.homeManagerModules.default
|
||||||
] ++ common;
|
] ++ common;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
15
home/natto/ags/README.md
Normal file
15
home/natto/ags/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
# Starter Config
|
||||||
|
|
||||||
|
if suggestions don't work, first make sure
|
||||||
|
you have TypeScript LSP working in your editor
|
||||||
|
|
||||||
|
if you do not want typechecking only suggestions
|
||||||
|
|
||||||
|
```json
|
||||||
|
// tsconfig.json
|
||||||
|
"checkJs": false
|
||||||
|
```
|
||||||
|
|
||||||
|
types are symlinked to:
|
||||||
|
/nix/store/4rpg1hbvvfb8wpxf1a6ljbm390wfcwcd-ags-1.8.2/share/com.github.Aylur.ags/types
|
31
home/natto/ags/config.js
Normal file
31
home/natto/ags/config.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import Bar from "./windows/bar/index.js";
|
||||||
|
import Settings from "./windows/settings/index.js";
|
||||||
|
import MusicBox from "./windows/music-box/index.js";
|
||||||
|
import Calendar from "./windows/calendar.js";
|
||||||
|
|
||||||
|
const configDir = App.configDir;
|
||||||
|
|
||||||
|
const scssStyle = `${configDir}/style.scss`;
|
||||||
|
const cssStyle = `${configDir}/style.css`;
|
||||||
|
|
||||||
|
const compileSass = () => {
|
||||||
|
Utils.exec(`scss ${scssStyle} ${cssStyle}`);
|
||||||
|
console.log("sass compiled to css");
|
||||||
|
};
|
||||||
|
|
||||||
|
compileSass();
|
||||||
|
|
||||||
|
Utils.monitorFile(`${configDir}/styles`, () => {
|
||||||
|
console.log("change detected in style");
|
||||||
|
compileSass();
|
||||||
|
App.resetCss();
|
||||||
|
App.applyCss(cssStyle);
|
||||||
|
console.log("new style applied");
|
||||||
|
});
|
||||||
|
|
||||||
|
App.config({
|
||||||
|
style: "./style.css",
|
||||||
|
windows: [Bar(), MusicBox(), Settings(), Calendar()],
|
||||||
|
});
|
||||||
|
|
||||||
|
export {};
|
6
home/natto/ags/constants.js
Normal file
6
home/natto/ags/constants.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const WindowNames = {
|
||||||
|
BAR: "bar",
|
||||||
|
SETTINGS: "settings",
|
||||||
|
MUSIC_BOX: "music-box",
|
||||||
|
CALENDAR: "calendar",
|
||||||
|
};
|
37
home/natto/ags/default.nix
Normal file
37
home/natto/ags/default.nix
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{ pkgs, lib, config, inputs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.programs.ags;
|
||||||
|
|
||||||
|
deps = with pkgs; [
|
||||||
|
sass
|
||||||
|
gawk
|
||||||
|
bash
|
||||||
|
procps
|
||||||
|
coreutils
|
||||||
|
imagemagick
|
||||||
|
config.wayland.windowManager.hyprland.package
|
||||||
|
] ++ lib.optional config.isLaptop brightnessctl;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
inputs.ags.homeManagerModules.default
|
||||||
|
];
|
||||||
|
|
||||||
|
programs.ags.enable = true;
|
||||||
|
|
||||||
|
systemd.user.services.ags = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Aylur's Gtk Shell";
|
||||||
|
PartOf = [
|
||||||
|
"tray.target"
|
||||||
|
"graphical-session.target"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Environment = "PATH=${lib.makeBinPath deps}";
|
||||||
|
ExecStart = "${cfg.package}/bin/ags";
|
||||||
|
Restart = "on-failure";
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
}
|
5
home/natto/ags/style.scss
Normal file
5
home/natto/ags/style.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@import "styles/global.scss";
|
||||||
|
@import "styles/bar.scss";
|
||||||
|
@import "styles/music-box.scss";
|
||||||
|
@import "styles/settings.scss";
|
||||||
|
@import "styles/calendar.scss";
|
40
home/natto/ags/styles/bar.scss
Normal file
40
home/natto/ags/styles/bar.scss
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.bar {
|
||||||
|
background: $background;
|
||||||
|
|
||||||
|
.hyprland {
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
button.focused {
|
||||||
|
transition-duration: 0;
|
||||||
|
color: $mauve;
|
||||||
|
box-shadow: 0 0 0 9999px rgba($mauve, 0.08) inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.unfocused {
|
||||||
|
background: none;
|
||||||
|
color: $flamingo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.music {
|
||||||
|
.music-title {
|
||||||
|
color: $sapphire;
|
||||||
|
}
|
||||||
|
|
||||||
|
.music-controls button {
|
||||||
|
color: $yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.network .network-icon {
|
||||||
|
color: $lavender;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-wrapper {
|
||||||
|
color: $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tray-button {
|
||||||
|
color: $flamingo;
|
||||||
|
}
|
||||||
|
}
|
8
home/natto/ags/styles/calendar.scss
Normal file
8
home/natto/ags/styles/calendar.scss
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.calendar {
|
||||||
|
.calendar-unwrapped {
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 2px solid $mauve;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba($base, 0.9);
|
||||||
|
}
|
||||||
|
}
|
57
home/natto/ags/styles/global.scss
Normal file
57
home/natto/ags/styles/global.scss
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
$rosewater: #f5e0dc;
|
||||||
|
$flamingo: #f2cdcd;
|
||||||
|
$pink: #f5c2e7;
|
||||||
|
$mauve: #cba6f7;
|
||||||
|
$red: #f38ba8;
|
||||||
|
$maroon: #eba0ac;
|
||||||
|
$peach: #fab387;
|
||||||
|
$yellow: #f9e2af;
|
||||||
|
$green: #a6e3a1;
|
||||||
|
$teal: #94e2d5;
|
||||||
|
$sky: #89dceb;
|
||||||
|
$sapphire: #74c7ec;
|
||||||
|
$blue: #89b4fa;
|
||||||
|
$lavender: #b4befe;
|
||||||
|
$text: #cdd6f4;
|
||||||
|
$subtext1: #bac2de;
|
||||||
|
$subtext0: #a6adc8;
|
||||||
|
$overlay2: #9399b2;
|
||||||
|
$overlay1: #7f849c;
|
||||||
|
$overlay0: #6c7086;
|
||||||
|
$surface2: #585b70;
|
||||||
|
$surface1: #45475a;
|
||||||
|
$surface0: #313244;
|
||||||
|
$base: #1e1e2e;
|
||||||
|
$mantle: #181825;
|
||||||
|
$crust: #11111b;
|
||||||
|
|
||||||
|
$background: $base;
|
||||||
|
$foreground: $text;
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar,
|
||||||
|
.music-box,
|
||||||
|
.calendar,
|
||||||
|
.settings {
|
||||||
|
font-size: 16px;
|
||||||
|
font-family: "Fira Mono";
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
.hyprland {
|
||||||
|
font-size: 18px;
|
||||||
|
font-family: "Lohit Gurmukhi";
|
||||||
|
}
|
||||||
|
}
|
79
home/natto/ags/styles/music-box.scss
Normal file
79
home/natto/ags/styles/music-box.scss
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
.music-box {
|
||||||
|
.music-box-unwrapped {
|
||||||
|
margin-top: 15px;
|
||||||
|
.music-player {
|
||||||
|
background: $base;
|
||||||
|
border: 2px solid $mauve;
|
||||||
|
border-radius: 13px;
|
||||||
|
.cover-art {
|
||||||
|
margin: 2em;
|
||||||
|
border-radius: 13px;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
.music-details {
|
||||||
|
$shadow: 0px 0px 10px rgba(black, 0.8);
|
||||||
|
padding: 2em;
|
||||||
|
.title {
|
||||||
|
font-size: 30px;
|
||||||
|
color: $pink;
|
||||||
|
text-shadow: $shadow;
|
||||||
|
}
|
||||||
|
.icon-wrapper {
|
||||||
|
.icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: $yellow;
|
||||||
|
}
|
||||||
|
.artist {
|
||||||
|
color: $yellow;
|
||||||
|
text-shadow: $shadow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.length-label,
|
||||||
|
.position-label {
|
||||||
|
text-shadow: $shadow;
|
||||||
|
}
|
||||||
|
.music-controls {
|
||||||
|
border-radius: 20px 8px 20px 8px;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 0px 10px;
|
||||||
|
background: rgba($base, 0.5);
|
||||||
|
button {
|
||||||
|
font-size: 30px;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scale trough {
|
||||||
|
min-height: 10px;
|
||||||
|
margin: 0 15px;
|
||||||
|
|
||||||
|
highlight {
|
||||||
|
background-image: linear-gradient(to right, $sapphire, $blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
slider {
|
||||||
|
border-radius: 4px;
|
||||||
|
background: $background;
|
||||||
|
margin: -10px -10px;
|
||||||
|
transition: 0.1s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 2px $mauve solid;
|
||||||
|
box-shadow: 0 0 0 8px rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px inset,
|
||||||
|
0 0 0 8px rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
home/natto/ags/styles/settings.scss
Normal file
112
home/natto/ags/styles/settings.scss
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
.settings {
|
||||||
|
.settings-unwrapped {
|
||||||
|
margin-top: 10px;
|
||||||
|
border: 2px solid $mauve;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: $base;
|
||||||
|
|
||||||
|
.metrics {
|
||||||
|
& > * {
|
||||||
|
background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
.metric-progress {
|
||||||
|
min-width: 40px;
|
||||||
|
min-height: 40px;
|
||||||
|
font-size: 4px;
|
||||||
|
margin: 4px;
|
||||||
|
.metric-icon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cpu-metric {
|
||||||
|
color: $mauve;
|
||||||
|
}
|
||||||
|
.memory-metric {
|
||||||
|
color: $flamingo;
|
||||||
|
}
|
||||||
|
.disk-metric {
|
||||||
|
color: $pink;
|
||||||
|
}
|
||||||
|
.battery-metric {
|
||||||
|
color: $yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-col {
|
||||||
|
.sliders {
|
||||||
|
$icon-size: 15px;
|
||||||
|
$scale-height: 20px;
|
||||||
|
|
||||||
|
margin: 10px;
|
||||||
|
scale trough {
|
||||||
|
min-height: $scale-height;
|
||||||
|
min-width: 160px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
highlight {
|
||||||
|
all: unset;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider {
|
||||||
|
$slider-height: $scale-height + 5px;
|
||||||
|
|
||||||
|
min-width: $slider-height;
|
||||||
|
min-height: $slider-height;
|
||||||
|
border-radius: $slider-height;
|
||||||
|
background: $text;
|
||||||
|
box-shadow: 0 2px 0 0 rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume {
|
||||||
|
.volume-icon {
|
||||||
|
font-size: $icon-size;
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
|
||||||
|
scale trough highlight {
|
||||||
|
background-color: $green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brightness {
|
||||||
|
.brightness-icon {
|
||||||
|
font-size: $icon-size;
|
||||||
|
color: $yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
scale trough highlight {
|
||||||
|
background-color: $yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-col-temps {
|
||||||
|
.temperature {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
.temperature-hot {
|
||||||
|
color: $red;
|
||||||
|
}
|
||||||
|
.weather {
|
||||||
|
color: $rosewater;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-menu {
|
||||||
|
margin: 20px;
|
||||||
|
button {
|
||||||
|
&:hover {
|
||||||
|
color: $red;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
home/natto/ags/tsconfig.json
Normal file
18
home/natto/ags/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"lib": [
|
||||||
|
"ES2022"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": false,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"typeRoots": [
|
||||||
|
"./types"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
42
home/natto/ags/utils/music.js
Normal file
42
home/natto/ags/utils/music.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import GLib from "gi://GLib";
|
||||||
|
|
||||||
|
export const lengthStr = (length) => {
|
||||||
|
if (length < 0) return "0:00";
|
||||||
|
|
||||||
|
const min = Math.floor(length / 60);
|
||||||
|
const sec = Math.floor(length % 60);
|
||||||
|
const sec0 = sec < 10 ? "0" : "";
|
||||||
|
return `${min}:${sec0}${sec}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blurBg = (cover) => {
|
||||||
|
if (!cover) return "";
|
||||||
|
|
||||||
|
const cachePath = Utils.CACHE_DIR + "/media";
|
||||||
|
const blurPath = cachePath + "/blur";
|
||||||
|
const bgPath = blurPath + cover.substring(cachePath.length);
|
||||||
|
|
||||||
|
if (!GLib.file_test(bgPath, GLib.FileTest.EXISTS)) {
|
||||||
|
Utils.ensureDirectory(blurPath);
|
||||||
|
Utils.exec(
|
||||||
|
`convert ${cover} -scale 10% -blur 0x2 -resize 1000% ${bgPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
background-image: url('${bgPath}');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findPlayer = (players) => {
|
||||||
|
const active = players.find((p) => p.playBackStatus === "Playing");
|
||||||
|
|
||||||
|
if (active) return active;
|
||||||
|
|
||||||
|
for (const p of players) if (p) return p;
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
41
home/natto/ags/utils/popup.js
Normal file
41
home/natto/ags/utils/popup.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export const Padding = (name, { hexpand = true, vexpand = true } = {}) =>
|
||||||
|
Widget.EventBox({
|
||||||
|
hexpand,
|
||||||
|
vexpand,
|
||||||
|
can_focus: false,
|
||||||
|
setup: (w) => w.on("button-press-event", () => App.closeWindow(name)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Revealer = ({
|
||||||
|
name,
|
||||||
|
child,
|
||||||
|
transition = "slide_down",
|
||||||
|
transitionDuration = 200,
|
||||||
|
}) =>
|
||||||
|
Widget.Box({
|
||||||
|
css: "padding: 1px;",
|
||||||
|
child: Widget.Revealer({
|
||||||
|
transition,
|
||||||
|
transitionDuration,
|
||||||
|
child: Widget.Box({
|
||||||
|
child,
|
||||||
|
}),
|
||||||
|
setup: (self) =>
|
||||||
|
self.hook(App, (_, window, visible) => {
|
||||||
|
if (window === name) self.reveal_child = visible;
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ({ name, layout, ...props }) =>
|
||||||
|
Widget.Window({
|
||||||
|
name,
|
||||||
|
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
|
||||||
|
visible: false,
|
||||||
|
keymode: "on-demand",
|
||||||
|
exclusivity: "normal",
|
||||||
|
layer: "top",
|
||||||
|
anchor: ["top", "bottom", "right", "left"],
|
||||||
|
child: layout,
|
||||||
|
...props,
|
||||||
|
});
|
5
home/natto/ags/utils/text.js
Normal file
5
home/natto/ags/utils/text.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const shrinkText = (str, n) => {
|
||||||
|
let newStr = str.substring(0, n);
|
||||||
|
if (str.length > n) newStr = newStr + "...";
|
||||||
|
return newStr;
|
||||||
|
};
|
19
home/natto/ags/windows/bar/bluetooth.js
Normal file
19
home/natto/ags/windows/bar/bluetooth.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const bluetooth = await Service.import("bluetooth");
|
||||||
|
|
||||||
|
export default () =>
|
||||||
|
Widget.Icon({
|
||||||
|
setup: (self) =>
|
||||||
|
self.hook(
|
||||||
|
bluetooth,
|
||||||
|
(self) => {
|
||||||
|
self.tooltipText = bluetooth.connected_devices
|
||||||
|
.map(({ name }) => name)
|
||||||
|
.join("\n");
|
||||||
|
self.visible = bluetooth.connected_devices.length > 0;
|
||||||
|
},
|
||||||
|
"notify::connected-devices",
|
||||||
|
),
|
||||||
|
icon: bluetooth
|
||||||
|
.bind("enabled")
|
||||||
|
.as((on) => `bluetooth-${on ? "active" : "disabled"}-symbolic`),
|
||||||
|
});
|
38
home/natto/ags/windows/bar/hyprland.js
Normal file
38
home/natto/ags/windows/bar/hyprland.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
const hyprland = await Service.import("hyprland");
|
||||||
|
|
||||||
|
const gurmukhiNums = {
|
||||||
|
1: "੧",
|
||||||
|
2: "੨",
|
||||||
|
3: "੩",
|
||||||
|
4: "੪",
|
||||||
|
5: "੫",
|
||||||
|
6: "੬",
|
||||||
|
7: "੭",
|
||||||
|
8: "੮",
|
||||||
|
9: "੯",
|
||||||
|
10: "੦",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const activeId = hyprland.active.workspace.bind("id");
|
||||||
|
const workspaces = hyprland.bind("workspaces").as((ws) =>
|
||||||
|
ws
|
||||||
|
.sort((a, b) => a.id - b.id)
|
||||||
|
.map(({ id }) =>
|
||||||
|
Widget.Button({
|
||||||
|
onClicked: () => hyprland.messageAsync(`dispatch workspace ${id}`),
|
||||||
|
child: Widget.Label(gurmukhiNums[id]),
|
||||||
|
className: activeId.as(
|
||||||
|
(i) => `${i === id ? "focused" : "unfocused"}`,
|
||||||
|
),
|
||||||
|
cursor: "pointer",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Widget.Box({
|
||||||
|
css: "padding: 1px;",
|
||||||
|
className: "hyprland",
|
||||||
|
children: workspaces,
|
||||||
|
});
|
||||||
|
};
|
56
home/natto/ags/windows/bar/index.js
Normal file
56
home/natto/ags/windows/bar/index.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import Hyprland from "./hyprland.js";
|
||||||
|
import Music from "./music.js";
|
||||||
|
import Tray from "./tray.js";
|
||||||
|
import Time from "./time.js";
|
||||||
|
import Network from "./network.js";
|
||||||
|
import Bluetooth from "./bluetooth.js";
|
||||||
|
import Settings from "./settings.js";
|
||||||
|
|
||||||
|
import { WindowNames } from "../../constants.js";
|
||||||
|
|
||||||
|
const { BAR } = WindowNames;
|
||||||
|
|
||||||
|
const Left = () => {
|
||||||
|
return Widget.Box({
|
||||||
|
className: "bar-left",
|
||||||
|
spacing: 8,
|
||||||
|
children: [Hyprland()],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const Center = (monitor) => {
|
||||||
|
return Widget.Box({
|
||||||
|
className: "bar-center",
|
||||||
|
spacing: 8,
|
||||||
|
children: [Music(monitor)],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const Right = (monitor) => {
|
||||||
|
return Widget.Box({
|
||||||
|
className: "bar-right",
|
||||||
|
hpack: "end",
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
Tray(),
|
||||||
|
Bluetooth(),
|
||||||
|
Network(),
|
||||||
|
Time(monitor),
|
||||||
|
Settings(monitor),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (monitor = 0) =>
|
||||||
|
Widget.Window({
|
||||||
|
name: `${BAR}-${monitor}`,
|
||||||
|
className: BAR,
|
||||||
|
monitor,
|
||||||
|
anchor: ["top", "left", "right"],
|
||||||
|
exclusivity: "exclusive",
|
||||||
|
child: Widget.CenterBox({
|
||||||
|
startWidget: Left(),
|
||||||
|
centerWidget: Center(monitor),
|
||||||
|
endWidget: Right(monitor),
|
||||||
|
}),
|
||||||
|
});
|
65
home/natto/ags/windows/bar/music.js
Normal file
65
home/natto/ags/windows/bar/music.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import Controls from "../music-box/music-controls.js";
|
||||||
|
import { shrinkText } from "../../utils/text.js";
|
||||||
|
import { findPlayer } from "../../utils/music.js";
|
||||||
|
import { WindowNames } from "../../constants.js";
|
||||||
|
|
||||||
|
const mpris = await Service.import("mpris");
|
||||||
|
const players = mpris.bind("players");
|
||||||
|
|
||||||
|
/** @param {import('types/service/mpris').MprisPlayer} player */
|
||||||
|
const Player = (player, monitor) => {
|
||||||
|
const revealer = Widget.Revealer({
|
||||||
|
revealChild: false,
|
||||||
|
transitionDuration: 300,
|
||||||
|
transition: "slide_left",
|
||||||
|
child: Controls(player),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.EventBox({
|
||||||
|
visible: player.bus_name === findPlayer(mpris.players).bus_name,
|
||||||
|
cursor: "pointer",
|
||||||
|
setup: (self) => {
|
||||||
|
self.on("leave-notify-event", () => {
|
||||||
|
revealer.reveal_child = false;
|
||||||
|
});
|
||||||
|
self.on("enter-notify-event", () => {
|
||||||
|
revealer.reveal_child = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Widget.Box({
|
||||||
|
className: "music",
|
||||||
|
children: [
|
||||||
|
Widget.Button({
|
||||||
|
onClicked: () =>
|
||||||
|
App.toggleWindow(`${WindowNames.MUSIC_BOX}-${monitor}`),
|
||||||
|
className: "music-title",
|
||||||
|
child: Widget.Label().hook(player, (self) => {
|
||||||
|
self.tooltip_text = player.track_title;
|
||||||
|
self.label = shrinkText(self.tooltip_text, 50);
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
revealer,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.hook(
|
||||||
|
mpris,
|
||||||
|
(self, bus_name) => {
|
||||||
|
self.visible = player.bus_name === bus_name;
|
||||||
|
},
|
||||||
|
"player-changed",
|
||||||
|
)
|
||||||
|
.hook(
|
||||||
|
mpris,
|
||||||
|
(self) => {
|
||||||
|
self.visible = player === findPlayer(mpris.players);
|
||||||
|
},
|
||||||
|
"player-closed",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default (monitor) =>
|
||||||
|
Widget.Box({
|
||||||
|
visible: players.as((p) => p.length > 0),
|
||||||
|
children: players.as((ps) => ps.map((p) => Player(p, monitor))),
|
||||||
|
});
|
40
home/natto/ags/windows/bar/network.js
Normal file
40
home/natto/ags/windows/bar/network.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const network = await Service.import("network");
|
||||||
|
|
||||||
|
const WifiIndicator = () =>
|
||||||
|
Widget.Box({
|
||||||
|
tooltipText: network.wifi.bind("state").as((s) => `State: ${s}`),
|
||||||
|
children: [
|
||||||
|
Widget.Icon({
|
||||||
|
className: "network-icon",
|
||||||
|
icon: network.wifi.bind("icon_name"),
|
||||||
|
}),
|
||||||
|
Widget.Label({
|
||||||
|
visible: network.wifi.bind("ssid"),
|
||||||
|
label: network.wifi.bind("ssid"),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const WiredIndicator = () =>
|
||||||
|
Widget.Icon({
|
||||||
|
className: "network-icon",
|
||||||
|
tooltipText: network.wired.bind("internet").as((a) => `Internet: ${a}`),
|
||||||
|
icon: network.wired.bind("icon_name"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () =>
|
||||||
|
Widget.Stack({
|
||||||
|
className: "network",
|
||||||
|
children: {
|
||||||
|
wifi: WifiIndicator(),
|
||||||
|
wired: WiredIndicator(),
|
||||||
|
},
|
||||||
|
shown: Utils.merge(
|
||||||
|
[network.bind("primary"), network.wired.bind("state")],
|
||||||
|
(primary, wired) => {
|
||||||
|
if (primary) return primary;
|
||||||
|
if (wired == "activated") return "wired";
|
||||||
|
return "wifi";
|
||||||
|
},
|
||||||
|
),
|
||||||
|
});
|
8
home/natto/ags/windows/bar/settings.js
Normal file
8
home/natto/ags/windows/bar/settings.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { WindowNames } from "../../constants.js";
|
||||||
|
|
||||||
|
export default (monitor) =>
|
||||||
|
Widget.Button({
|
||||||
|
child: Widget.Icon("open-menu-symbolic"),
|
||||||
|
onPrimaryClick: () =>
|
||||||
|
App.toggleWindow(`${WindowNames.SETTINGS}-${monitor}`),
|
||||||
|
});
|
42
home/natto/ags/windows/bar/time.js
Normal file
42
home/natto/ags/windows/bar/time.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const time = Variable("", {
|
||||||
|
poll: [1000, 'date "+%H:%M:%S"'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const getDate = () => " " + Utils.exec('date "+%a, %b %e"');
|
||||||
|
const date = Variable(getDate());
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const revealer = Widget.Revealer({
|
||||||
|
revealChild: false,
|
||||||
|
transitionDuration: 300,
|
||||||
|
transition: "slide_left",
|
||||||
|
child: Widget.Label({
|
||||||
|
label: date.bind(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.EventBox({
|
||||||
|
cursor: "pointer",
|
||||||
|
setup: (self) => {
|
||||||
|
self.on("leave-notify-event", () => {
|
||||||
|
revealer.reveal_child = false;
|
||||||
|
});
|
||||||
|
self.on("enter-notify-event", () => {
|
||||||
|
date.value = getDate();
|
||||||
|
revealer.reveal_child = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Widget.Button({
|
||||||
|
className: "date-wrapper",
|
||||||
|
onPrimaryClick: () => App.toggleWindow("calendar-0"),
|
||||||
|
child: Widget.Box({
|
||||||
|
children: [
|
||||||
|
Widget.Label({
|
||||||
|
label: time.bind(),
|
||||||
|
}),
|
||||||
|
revealer,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
17
home/natto/ags/windows/bar/tray.js
Normal file
17
home/natto/ags/windows/bar/tray.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const systemtray = await Service.import("systemtray");
|
||||||
|
|
||||||
|
const SysTrayItem = (item) =>
|
||||||
|
Widget.Button({
|
||||||
|
className: "system-tray-item",
|
||||||
|
child: Widget.Icon().bind("icon", item, "icon"),
|
||||||
|
tooltipMarkup: item.bind("tooltip_markup"),
|
||||||
|
onPrimaryClick: (_, event) => item.activate(event),
|
||||||
|
onSecondaryClick: (_, event) => item.openMenu(event),
|
||||||
|
cursor: "pointer",
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () =>
|
||||||
|
Widget.Box({
|
||||||
|
className: "system-tray-unwrapped",
|
||||||
|
children: systemtray.bind("items").as((i) => i.map(SysTrayItem)),
|
||||||
|
});
|
37
home/natto/ags/windows/calendar.js
Normal file
37
home/natto/ags/windows/calendar.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import Popup, { Padding, Revealer } from "../utils/popup.js";
|
||||||
|
import { WindowNames } from "../constants.js";
|
||||||
|
|
||||||
|
const Tray = Widget.Calendar({
|
||||||
|
className: "calendar-unwrapped",
|
||||||
|
showDayNames: true,
|
||||||
|
showHeading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default (monitor = 0) => {
|
||||||
|
const { CALENDAR } = WindowNames;
|
||||||
|
const name = `${CALENDAR}-${monitor}`;
|
||||||
|
|
||||||
|
return Popup({
|
||||||
|
name,
|
||||||
|
className: CALENDAR,
|
||||||
|
monitor,
|
||||||
|
layout: Widget.Box({
|
||||||
|
children: [
|
||||||
|
Padding(name),
|
||||||
|
Widget.Box({
|
||||||
|
hexpand: false,
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Revealer({
|
||||||
|
name,
|
||||||
|
child: Tray,
|
||||||
|
transition: "slide_down",
|
||||||
|
transitionDuration: 400,
|
||||||
|
}),
|
||||||
|
Padding(name),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
183
home/natto/ags/windows/music-box/index.js
Normal file
183
home/natto/ags/windows/music-box/index.js
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
// Mostly taken from https://github.com/Aylur/ags/blob/11150225e62462bcd431d1e55185e810190a730a/example/media-widget/Media.js
|
||||||
|
|
||||||
|
import Popup, { Padding, Revealer } from "../../utils/popup.js";
|
||||||
|
import { shrinkText } from "../../utils/text.js";
|
||||||
|
import { lengthStr, blurBg } from "../../utils/music.js";
|
||||||
|
import { findPlayer } from "../../utils/music.js";
|
||||||
|
import { WindowNames } from "../../constants.js";
|
||||||
|
import Controls from "./music-controls.js";
|
||||||
|
|
||||||
|
const FALLBACK_ICON = "audio-x-generic-symbolic";
|
||||||
|
const { MUSIC_BOX } = WindowNames;
|
||||||
|
|
||||||
|
const mpris = await Service.import("mpris");
|
||||||
|
|
||||||
|
const Player = (player) => {
|
||||||
|
const img = Widget.Box({
|
||||||
|
visible: player.bind("cover_path"),
|
||||||
|
className: "cover-art",
|
||||||
|
vpack: "start",
|
||||||
|
css: player
|
||||||
|
.bind("cover_path")
|
||||||
|
.as(
|
||||||
|
(p) =>
|
||||||
|
(p ? "min-width: 200px; min-height: 200px;" : "") +
|
||||||
|
`background-image: url('${p}');`,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = Widget.Label({
|
||||||
|
className: "title",
|
||||||
|
wrap: true,
|
||||||
|
hpack: "start",
|
||||||
|
label: player.bind("track_title").as((t) => shrinkText(t, 40)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const artist = Widget.Label({
|
||||||
|
className: "artist",
|
||||||
|
wrap: true,
|
||||||
|
hpack: "start",
|
||||||
|
label: player.bind("track_artists").as((a) => shrinkText(a.join(", "), 80)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const positionSlider = Widget.Slider({
|
||||||
|
className: "position",
|
||||||
|
drawValue: false,
|
||||||
|
onChange: ({ value }) => (player.position = value * player.length),
|
||||||
|
visible: player.bind("length").as((l) => l > 0),
|
||||||
|
setup: (self) => {
|
||||||
|
const update = () => {
|
||||||
|
if (self.dragging) return;
|
||||||
|
|
||||||
|
const value = player.position / player.length;
|
||||||
|
self.value = value > 0 ? value : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
self
|
||||||
|
.hook(player, update)
|
||||||
|
.hook(player, update, "position")
|
||||||
|
.poll(1000, update);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const positionLabel = Widget.Label({
|
||||||
|
ypad: 0,
|
||||||
|
hpack: "start",
|
||||||
|
className: "position-label",
|
||||||
|
setup: (self) => {
|
||||||
|
const update = (_, time) => {
|
||||||
|
self.label = lengthStr(time || player.position);
|
||||||
|
self.visible = player.length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.hook(player, update, "position");
|
||||||
|
self.poll(1000, update);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const lengthLabel = Widget.Label({
|
||||||
|
ypad: 0,
|
||||||
|
hpack: "end",
|
||||||
|
className: "length-label",
|
||||||
|
visible: player.bind("length").as((l) => l > 0),
|
||||||
|
label: player.bind("length").as(lengthStr),
|
||||||
|
});
|
||||||
|
|
||||||
|
const icon = Widget.Icon({
|
||||||
|
className: "icon",
|
||||||
|
hexpand: true,
|
||||||
|
hpack: "end",
|
||||||
|
vpack: "start",
|
||||||
|
tooltipText: player.identity || "",
|
||||||
|
icon: player.bind("entry").as((entry) => {
|
||||||
|
const name = `${entry}-symbolic`;
|
||||||
|
return Utils.lookUpIcon(name) ? name : FALLBACK_ICON;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.Box(
|
||||||
|
{
|
||||||
|
className: "music-player",
|
||||||
|
visible: player.bus_name === findPlayer(mpris.players).bus_name,
|
||||||
|
css: player.bind("cover_path").as(blurBg),
|
||||||
|
},
|
||||||
|
img,
|
||||||
|
Widget.CenterBox({
|
||||||
|
className: "music-details",
|
||||||
|
vertical: true,
|
||||||
|
hexpand: true,
|
||||||
|
spacing: 25,
|
||||||
|
startWidget: Widget.Box(
|
||||||
|
{
|
||||||
|
vertical: true,
|
||||||
|
spacing: 6,
|
||||||
|
},
|
||||||
|
title,
|
||||||
|
Widget.Box({
|
||||||
|
className: "icon-wrapper",
|
||||||
|
spacing: 10,
|
||||||
|
children: [artist, icon],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
centerWidget: positionSlider,
|
||||||
|
endWidget: Widget.CenterBox({
|
||||||
|
spacing: 6,
|
||||||
|
startWidget: positionLabel,
|
||||||
|
centerWidget: Controls(player),
|
||||||
|
endWidget: lengthLabel,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.hook(
|
||||||
|
mpris,
|
||||||
|
(self, bus_name) => {
|
||||||
|
self.visible = player.bus_name === bus_name;
|
||||||
|
},
|
||||||
|
"player-changed",
|
||||||
|
)
|
||||||
|
.hook(
|
||||||
|
mpris,
|
||||||
|
(self) => {
|
||||||
|
self.visible = player.bus_name === findPlayer(mpris.players).bus_name;
|
||||||
|
},
|
||||||
|
"player-closed",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlayerBox = () =>
|
||||||
|
Widget.Box({
|
||||||
|
className: `${MUSIC_BOX}-unwrapped`,
|
||||||
|
vertical: true,
|
||||||
|
css: "padding: 1px;",
|
||||||
|
children: mpris.bind("players").as((ps) => ps.map(Player)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default (monitor = 0) => {
|
||||||
|
const name = `${MUSIC_BOX}-${monitor}`;
|
||||||
|
|
||||||
|
return Popup({
|
||||||
|
name,
|
||||||
|
className: MUSIC_BOX,
|
||||||
|
monitor,
|
||||||
|
layout: Widget.Box({
|
||||||
|
children: [
|
||||||
|
Padding(name), // left
|
||||||
|
Widget.Box({
|
||||||
|
hexpand: false,
|
||||||
|
vexpand: false,
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Revealer({
|
||||||
|
name,
|
||||||
|
child: PlayerBox(),
|
||||||
|
transition: "slide_down",
|
||||||
|
transitionDuration: 400,
|
||||||
|
}),
|
||||||
|
Padding(name), // down
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
Padding(name), // right
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
47
home/natto/ags/windows/music-box/music-controls.js
Normal file
47
home/natto/ags/windows/music-box/music-controls.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
const PLAY_ICON = "media-playback-start-symbolic";
|
||||||
|
const PAUSE_ICON = "media-playback-pause-symbolic";
|
||||||
|
const PREV_ICON = "go-previous-symbolic";
|
||||||
|
const NEXT_ICON = "go-next-symbolic";
|
||||||
|
|
||||||
|
export default (player) => {
|
||||||
|
const playPause = Widget.Button({
|
||||||
|
class_name: "play-pause",
|
||||||
|
on_clicked: () => player.playPause(),
|
||||||
|
visible: player.bind("can_play"),
|
||||||
|
cursor: "pointer",
|
||||||
|
child: Widget.Icon({
|
||||||
|
icon: player.bind("play_back_status").transform((s) => {
|
||||||
|
switch (s) {
|
||||||
|
case "Playing":
|
||||||
|
return PAUSE_ICON;
|
||||||
|
case "Paused":
|
||||||
|
case "Stopped":
|
||||||
|
return PLAY_ICON;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const prev = Widget.Button({
|
||||||
|
yalign: 0.5,
|
||||||
|
onClicked: () => player.previous(),
|
||||||
|
visible: player.bind("can_go_prev"),
|
||||||
|
child: Widget.Icon(PREV_ICON),
|
||||||
|
cursor: "pointer",
|
||||||
|
});
|
||||||
|
|
||||||
|
const next = Widget.Button({
|
||||||
|
onClicked: () => player.next(),
|
||||||
|
visible: player.bind("can_go_next"),
|
||||||
|
child: Widget.Icon(NEXT_ICON),
|
||||||
|
cursor: "pointer",
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.CenterBox({
|
||||||
|
vertical: true,
|
||||||
|
centerWidget: Widget.Box({
|
||||||
|
className: "music-controls",
|
||||||
|
children: [prev, playPause, next],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
69
home/natto/ags/windows/settings/audio.js
Normal file
69
home/natto/ags/windows/settings/audio.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
const audio = await Service.import("audio");
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const isSpeaker = Variable(true);
|
||||||
|
|
||||||
|
/** @param {'speaker' | 'microphone'} type */
|
||||||
|
const VolumeSlider = (type = "speaker") =>
|
||||||
|
Widget.Slider({
|
||||||
|
hexpand: true,
|
||||||
|
drawValue: false,
|
||||||
|
onChange: ({ value }) => (audio[type].volume = value),
|
||||||
|
value: audio[type].bind("volume"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const speakerSlider = VolumeSlider("speaker");
|
||||||
|
const micSlider = VolumeSlider("microphone");
|
||||||
|
|
||||||
|
const speakerIndicator = Widget.Button({
|
||||||
|
on_clicked: () => (audio.speaker.is_muted = !audio.speaker.is_muted),
|
||||||
|
child: Widget.Icon().hook(audio.speaker, (self) => {
|
||||||
|
self.className = "volume-icon";
|
||||||
|
const vol = audio.speaker.volume * 100;
|
||||||
|
let icon = [
|
||||||
|
[101, "overamplified"],
|
||||||
|
[67, "high"],
|
||||||
|
[34, "medium"],
|
||||||
|
[1, "low"],
|
||||||
|
[0, "muted"],
|
||||||
|
].find(([threshold]) => threshold <= vol)?.[1];
|
||||||
|
|
||||||
|
if (audio.speaker.is_muted) icon = "muted";
|
||||||
|
|
||||||
|
self.icon = `audio-volume-${icon}-symbolic`;
|
||||||
|
self.tooltip_text = `Volume ${Math.floor(vol)}%`;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const micIndicator = Widget.Button({
|
||||||
|
on_clicked: () => (audio.microphone.is_muted = !audio.microphone.is_muted),
|
||||||
|
child: Widget.Icon().hook(audio.microphone, (self) => {
|
||||||
|
self.className = "volume-icon";
|
||||||
|
const vol = audio.microphone.volume * 100;
|
||||||
|
let icon = [
|
||||||
|
[67, "high"],
|
||||||
|
[34, "medium"],
|
||||||
|
[1, "low"],
|
||||||
|
[0, "muted"],
|
||||||
|
].find(([threshold]) => threshold <= vol)?.[1];
|
||||||
|
|
||||||
|
if (audio.microphone.is_muted) icon = "muted";
|
||||||
|
|
||||||
|
self.icon = `microphone-sensitivity-${icon}-symbolic`;
|
||||||
|
self.tooltip_text = `Volume ${Math.floor(vol)}%`;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.EventBox({
|
||||||
|
cursor: "pointer",
|
||||||
|
className: "volume",
|
||||||
|
onSecondaryClick: () => (isSpeaker.value = !isSpeaker.value),
|
||||||
|
child: Widget.Stack({
|
||||||
|
children: {
|
||||||
|
speaker: Widget.Box({}, speakerSlider, speakerIndicator),
|
||||||
|
microphone: Widget.Box({}, micSlider, micIndicator),
|
||||||
|
},
|
||||||
|
shown: isSpeaker.bind().as((s) => (s ? "speaker" : "microphone")),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
48
home/natto/ags/windows/settings/backlight.js
Normal file
48
home/natto/ags/windows/settings/backlight.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const hasBacklight = Variable(Utils.exec("ls /sys/class/backlight"));
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const getBrightness = () => {
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
Number(Utils.exec("brightnessctl get")) /
|
||||||
|
Number(Utils.exec("brightnessctl max"))
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
console.log("settings/backlight: failed to get brightness");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setBrightness = (b) => {
|
||||||
|
if (b < 0.05) b = 0.05;
|
||||||
|
else if (b > 1) b = 1;
|
||||||
|
|
||||||
|
Utils.exec(`brightnessctl set ${b * 100}%`);
|
||||||
|
};
|
||||||
|
const brightness = Variable(getBrightness());
|
||||||
|
|
||||||
|
const Slider = Widget.Slider({
|
||||||
|
hexpand: true,
|
||||||
|
drawValue: false,
|
||||||
|
onChange: ({ value }) => setBrightness(value),
|
||||||
|
value: brightness.bind(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const Indicator = Widget.Button({
|
||||||
|
on_clicked: brightness.bind().as((b) => () => {
|
||||||
|
if (b <= 0.5) brightness.value = 1;
|
||||||
|
else brightness.value = 0.5;
|
||||||
|
}),
|
||||||
|
child: Widget.Icon().hook(brightness, (self) => {
|
||||||
|
self.className = "brightness-icon";
|
||||||
|
self.icon = `display-brightness-symbolic`;
|
||||||
|
self.tooltip_text = `Brightness: ${Math.floor(brightness.value * 100)}%`;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.Box({
|
||||||
|
className: "brightness",
|
||||||
|
visible: hasBacklight.bind().as((b) => !!b),
|
||||||
|
children: [Slider, Indicator],
|
||||||
|
});
|
||||||
|
};
|
72
home/natto/ags/windows/settings/index.js
Normal file
72
home/natto/ags/windows/settings/index.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import Popup, { Padding, Revealer } from "../../utils/popup.js";
|
||||||
|
import { WindowNames } from "../../constants.js";
|
||||||
|
import Audio from "./audio.js";
|
||||||
|
import Backlight from "./backlight.js";
|
||||||
|
import {
|
||||||
|
cpuMetric,
|
||||||
|
memoryMetric,
|
||||||
|
diskMetric,
|
||||||
|
batteryMetric,
|
||||||
|
} from "./metrics.js";
|
||||||
|
import Temperature from "./temperature.js";
|
||||||
|
import Weather from "./weather.js";
|
||||||
|
import PowerMenu from "./power-menu.js";
|
||||||
|
|
||||||
|
const metrics = Widget.Box({
|
||||||
|
className: "metrics",
|
||||||
|
vertical: true,
|
||||||
|
children: [cpuMetric, memoryMetric, diskMetric, batteryMetric],
|
||||||
|
});
|
||||||
|
|
||||||
|
const sliders = Widget.Box({
|
||||||
|
className: "sliders",
|
||||||
|
vertical: true,
|
||||||
|
children: [Audio(), Backlight()],
|
||||||
|
});
|
||||||
|
|
||||||
|
const settingsCol = Widget.CenterBox({
|
||||||
|
className: "settings-col",
|
||||||
|
vertical: true,
|
||||||
|
spacing: 8,
|
||||||
|
startWidget: sliders,
|
||||||
|
centerWidget: Widget.CenterBox({
|
||||||
|
className: "settings-col-temps",
|
||||||
|
startWidget: Temperature(),
|
||||||
|
endWidget: Weather(),
|
||||||
|
}),
|
||||||
|
endWidget: PowerMenu(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const settings = Widget.Box({
|
||||||
|
className: "settings-unwrapped",
|
||||||
|
children: [metrics, settingsCol],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default (monitor = 0) => {
|
||||||
|
const { SETTINGS } = WindowNames;
|
||||||
|
const name = `${SETTINGS}-${monitor}`;
|
||||||
|
|
||||||
|
return Popup({
|
||||||
|
name,
|
||||||
|
className: SETTINGS,
|
||||||
|
monitor,
|
||||||
|
layout: Widget.Box({
|
||||||
|
children: [
|
||||||
|
Padding(name),
|
||||||
|
Widget.Box({
|
||||||
|
hexpand: false,
|
||||||
|
vertical: true,
|
||||||
|
children: [
|
||||||
|
Revealer({
|
||||||
|
name,
|
||||||
|
child: settings,
|
||||||
|
transition: "slide_down",
|
||||||
|
transitionDuration: 400,
|
||||||
|
}),
|
||||||
|
Padding(name),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
};
|
104
home/natto/ags/windows/settings/metrics.js
Normal file
104
home/natto/ags/windows/settings/metrics.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
const battery = await Service.import("battery");
|
||||||
|
|
||||||
|
const divide = ([total, free]) => free / total;
|
||||||
|
|
||||||
|
const cpuValue = Variable(0, {
|
||||||
|
poll: [
|
||||||
|
2000,
|
||||||
|
"top -b -n 1",
|
||||||
|
(out) =>
|
||||||
|
divide([
|
||||||
|
100,
|
||||||
|
out
|
||||||
|
.split("\n")
|
||||||
|
.find((line) => line.includes("Cpu(s)"))
|
||||||
|
.split(/\s+/)[1]
|
||||||
|
.replace(",", "."),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const memoryValue = Variable([0, 0], {
|
||||||
|
poll: [
|
||||||
|
2000,
|
||||||
|
"free",
|
||||||
|
(out) => {
|
||||||
|
const data = out
|
||||||
|
.split("\n")
|
||||||
|
.find((line) => line.includes("Mem:"))
|
||||||
|
.split(/\s+/)
|
||||||
|
.splice(1, 2);
|
||||||
|
return [(data[1] / (1024 * 1024)).toFixed(2), divide(data)];
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const diskValue = Variable(["0G", "0%"], {
|
||||||
|
poll: [
|
||||||
|
120000,
|
||||||
|
"df -kh /",
|
||||||
|
(out) => out.split("\n")[1].split(/\s+/).splice(3, 2),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const mkMetric = ({
|
||||||
|
className,
|
||||||
|
tooltipText,
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
visible = true,
|
||||||
|
}) =>
|
||||||
|
Widget.Box(
|
||||||
|
{
|
||||||
|
spacing: 10,
|
||||||
|
className,
|
||||||
|
tooltipText,
|
||||||
|
visible,
|
||||||
|
},
|
||||||
|
Widget.CircularProgress({
|
||||||
|
className: "metric-progress",
|
||||||
|
child: Widget.Icon({
|
||||||
|
icon,
|
||||||
|
className: "metric-icon",
|
||||||
|
}),
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
Widget.Label({
|
||||||
|
wrap: true,
|
||||||
|
label,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const cpuMetric = mkMetric({
|
||||||
|
className: "cpu-metric",
|
||||||
|
tooltipText: cpuValue.bind().as((c) => `CPU: ${(c * 100).toFixed(2)}%`),
|
||||||
|
value: cpuValue.bind(),
|
||||||
|
label: cpuValue.bind().as((c) => `${(c * 100).toFixed(2)}%`),
|
||||||
|
icon: "cpu-symbolic",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const memoryMetric = mkMetric({
|
||||||
|
className: "memory-metric",
|
||||||
|
tooltipText: memoryValue.bind().as((m) => `RAM :${m[0]}G`),
|
||||||
|
value: memoryValue.bind().as((m) => m[1]),
|
||||||
|
label: memoryValue.bind().as((m) => `${m[0]}G`),
|
||||||
|
icon: "memory-symbolic",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const diskMetric = mkMetric({
|
||||||
|
className: "disk-metric",
|
||||||
|
tooltipText: diskValue.bind().as((d) => `Free Space :${d[0]}`),
|
||||||
|
value: diskValue.bind().as((d) => Number(d[1]) / 100),
|
||||||
|
label: diskValue.bind().as((d) => d[1]),
|
||||||
|
icon: "drive-harddisk-symbolic",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const batteryMetric = mkMetric({
|
||||||
|
className: "battery-metric",
|
||||||
|
tooltipText: battery.bind("percent").as((p) => `Battery: ${p}%`),
|
||||||
|
value: battery.bind("percent").as((p) => (p > 0 ? p / 100 : 0)),
|
||||||
|
label: battery.bind("percent").as((p) => `${p}%`),
|
||||||
|
icon: battery.bind("icon_name"),
|
||||||
|
visible: battery.bind("available"),
|
||||||
|
});
|
94
home/natto/ags/windows/settings/power-menu.js
Normal file
94
home/natto/ags/windows/settings/power-menu.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
export default () => {
|
||||||
|
const isLocked = Variable(true);
|
||||||
|
const power = Variable("poweroff");
|
||||||
|
const suspend = Variable("sleep");
|
||||||
|
|
||||||
|
const cursor = "pointer";
|
||||||
|
|
||||||
|
const unlockButton = Widget.Button({
|
||||||
|
onPrimaryClick: () => {
|
||||||
|
isLocked.value = false;
|
||||||
|
},
|
||||||
|
tooltipText: "Unock power menu",
|
||||||
|
child: Widget.Icon("system-lock-screen-symbolic"),
|
||||||
|
cursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lockButton = Widget.Button({
|
||||||
|
onPrimaryClick: () => {
|
||||||
|
isLocked.value = true;
|
||||||
|
},
|
||||||
|
tooltipText: "Lock power menu",
|
||||||
|
child: Widget.Icon("system-lock-screen-symbolic"),
|
||||||
|
cursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const poweroffButton = Widget.Button({
|
||||||
|
onPrimaryClick: () => {
|
||||||
|
Utils.exec("poweroff");
|
||||||
|
},
|
||||||
|
onSecondaryClick: () => (power.value = "reboot"),
|
||||||
|
tooltipText: "Shutdown",
|
||||||
|
child: Widget.Icon("system-shutdown-symbolic"),
|
||||||
|
cursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rebootButton = Widget.Button({
|
||||||
|
onPrimaryClick: () => {
|
||||||
|
Utils.exec("reboot");
|
||||||
|
},
|
||||||
|
onSecondaryClick: () => (power.value = "poweroff"),
|
||||||
|
tooltipText: "Reboot",
|
||||||
|
child: Widget.Icon("system-reboot-symbolic"),
|
||||||
|
cursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sleepButton = Widget.Button({
|
||||||
|
onPrimaryClick: () => {
|
||||||
|
Utils.exec("systemctl suspend");
|
||||||
|
},
|
||||||
|
onSecondaryClick: () => (suspend.value = "hibernate"),
|
||||||
|
tooltipText: "Sleep",
|
||||||
|
child: Widget.Icon("weather-clear-night-symbolic"),
|
||||||
|
cursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hibernateButton = Widget.Button({
|
||||||
|
onPrimaryClick: () => {
|
||||||
|
Utils.exec("systemctl hibernate");
|
||||||
|
},
|
||||||
|
onSecondaryClick: () => (suspend.value = "sleep"),
|
||||||
|
tooltipText: "Hibernate",
|
||||||
|
child: Widget.Icon("computer-symbolic"),
|
||||||
|
cursor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const powerStack = Widget.Stack({
|
||||||
|
children: {
|
||||||
|
poweroff: poweroffButton,
|
||||||
|
reboot: rebootButton,
|
||||||
|
},
|
||||||
|
shown: power.bind(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const suspendStack = Widget.Stack({
|
||||||
|
children: {
|
||||||
|
sleep: sleepButton,
|
||||||
|
hibernate: hibernateButton,
|
||||||
|
},
|
||||||
|
shown: suspend.bind(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.Stack({
|
||||||
|
className: "power-menu",
|
||||||
|
children: {
|
||||||
|
locked: Widget.CenterBox({ centerWidget: unlockButton }),
|
||||||
|
unlocked: Widget.CenterBox({
|
||||||
|
startWidget: powerStack,
|
||||||
|
centerWidget: lockButton,
|
||||||
|
endWidget: suspendStack,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
shown: isLocked.bind().as((l) => (l ? "locked" : "unlocked")),
|
||||||
|
});
|
||||||
|
};
|
62
home/natto/ags/windows/settings/temperature.js
Normal file
62
home/natto/ags/windows/settings/temperature.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
export default () => {
|
||||||
|
const getThermalZone = () => {
|
||||||
|
try {
|
||||||
|
return Utils.exec([
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
`awk '{print FILENAME ":" $0}' /sys/class/thermal/thermal_zone*/type`,
|
||||||
|
])
|
||||||
|
.split("\n")
|
||||||
|
.find((line) => line.includes("x86_pkg_temp"))
|
||||||
|
.split(":")[0]
|
||||||
|
.slice(0, -4);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
console.log("settings/temperature: cannot get thermal zone");
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const thermalZone = Variable(getThermalZone());
|
||||||
|
|
||||||
|
const tempValue = thermalZone.value
|
||||||
|
? Variable(0, {
|
||||||
|
poll: [
|
||||||
|
5000,
|
||||||
|
() => {
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
Utils.readFile(`${thermalZone.value}/temp`) / 1000
|
||||||
|
).toFixed(2);
|
||||||
|
} catch {
|
||||||
|
console.log(
|
||||||
|
"settings/temperature: specified thermal zone does not exist",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
: Variable(undefined);
|
||||||
|
|
||||||
|
return Widget.CenterBox({
|
||||||
|
vertical: true,
|
||||||
|
visible: thermalZone.bind().as((t) => !!t),
|
||||||
|
centerWidget: Widget.Box(
|
||||||
|
{
|
||||||
|
vertical: true,
|
||||||
|
spacing: 8,
|
||||||
|
tooltipText: tempValue.bind().as((t) => `CPU Temperature: ${t}°C`),
|
||||||
|
className: tempValue
|
||||||
|
.bind()
|
||||||
|
.as((t) => `temperature${t > 65 ? "-hot" : ""}`),
|
||||||
|
},
|
||||||
|
Widget.Icon({
|
||||||
|
icon: "sensors-temperature-symbolic",
|
||||||
|
}),
|
||||||
|
Widget.Label({
|
||||||
|
label: tempValue.bind().as((t) => `${t}°C`),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
78
home/natto/ags/windows/settings/weather.js
Normal file
78
home/natto/ags/windows/settings/weather.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const conditionIcons = {
|
||||||
|
Clear: "clear",
|
||||||
|
Sunny: "clear",
|
||||||
|
"Partly Cloudy": "few-clouds",
|
||||||
|
Cloudy: "overcast",
|
||||||
|
Overcast: "overcast",
|
||||||
|
"Light Rain": "showers-scattered",
|
||||||
|
Drizzle: "showers-scattered",
|
||||||
|
Rain: "showers",
|
||||||
|
"Heavy Rain": "showers",
|
||||||
|
Showers: "showers",
|
||||||
|
Thunderstorm: "storm",
|
||||||
|
Snow: "snow",
|
||||||
|
"Light Snow": "snow",
|
||||||
|
"Heavy Snow": "snow",
|
||||||
|
Mist: "fog",
|
||||||
|
Fog: "fog",
|
||||||
|
Haze: "fog",
|
||||||
|
Dust: "fog",
|
||||||
|
Smoke: "fog",
|
||||||
|
Sand: "fog",
|
||||||
|
Wind: "windy",
|
||||||
|
Tornado: "tornado",
|
||||||
|
undefined: "clear",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchWeather = async () => {
|
||||||
|
return await Utils.fetch("http://wttr.in/?format=j1")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((j) => j["current_condition"][0])
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
console.log("settings/weather: error fetching weather data");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const data = Variable(undefined, {
|
||||||
|
poll: [600000, async () => await fetchWeather()],
|
||||||
|
});
|
||||||
|
|
||||||
|
return Widget.Box(
|
||||||
|
{
|
||||||
|
vertical: true,
|
||||||
|
visible: data.bind().as((d) => !!d),
|
||||||
|
className: "weather",
|
||||||
|
},
|
||||||
|
Widget.Icon({
|
||||||
|
icon: data.bind().as((d) => {
|
||||||
|
const condition = d?.["weatherDesc"]?.[0]?.["value"];
|
||||||
|
return `weather-${conditionIcons[condition]}-symbolic`;
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Widget.Label({
|
||||||
|
label: data.bind().as((d) => {
|
||||||
|
const conditions = d?.["weatherDesc"]?.map((w) => w["value"]) || [];
|
||||||
|
|
||||||
|
return conditions.join(" ");
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Widget.Label({
|
||||||
|
label: data.bind().as((d) => {
|
||||||
|
const temperature = d?.["temp_C"];
|
||||||
|
const feelsLike = d?.["FeelsLikeC"];
|
||||||
|
|
||||||
|
return `${temperature}°C (${feelsLike}°C)`;
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Widget.Label({
|
||||||
|
label: data.bind().as((d) => {
|
||||||
|
const humidity = d?.["humidity"];
|
||||||
|
const precipitation = d?.["precipMM"];
|
||||||
|
|
||||||
|
return `${humidity}%, ${precipitation}mm`;
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
@@ -1,6 +1,7 @@
|
|||||||
{ pkgs, config, conf, inputs, ... }:
|
{ pkgs, config, conf, inputs, ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./ags
|
||||||
# ./eww
|
# ./eww
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user