diff --git a/README.md b/README.md
index 23d3df5..b41271b 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ include_toc: true
#### Programs
-# TODO: add a file manager
+# TODO: add a file manager and desktop manager
| Name | Type |
| -----------------: | :------------------ |
@@ -23,6 +23,7 @@ include_toc: true
| Neovim | Text editor |
| Firefox | Web browser |
| qBittorrent | Torrent client |
+| mpv | Video player |
| LibreOffice | Office suite |
| GIMP | Photo editor |
| Inkscape | Vector image editor |
diff --git a/common/home.nix b/common/home.nix
index b2de5c3..df4f0c4 100644
--- a/common/home.nix
+++ b/common/home.nix
@@ -1,4 +1,4 @@
-{ pkgs, config, inputs, ... }: {
+{ pkgs, config, ... }: {
home = {
username = "avery";
homeDirectory = "/home/avery";
diff --git a/common/nixos.nix b/common/nixos.nix
index 9676bb7..26ab9f5 100644
--- a/common/nixos.nix
+++ b/common/nixos.nix
@@ -17,6 +17,7 @@
users = {
defaultUserShell = pkgs.zsh;
users.avery = {
+ description = "Avery";
extraGroups = [ "wheel" ];
isNormalUser = true;
hashedPasswordFile = config.sops.secrets.avery_password.path;
diff --git a/common/zsh/powerline10k/p10k.zsh b/common/zsh/powerline10k/p10k.zsh
index 503b77c..5b3f90b 100644
--- a/common/zsh/powerline10k/p10k.zsh
+++ b/common/zsh/powerline10k/p10k.zsh
@@ -67,7 +67,7 @@
typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}'
fi
- typeset -g POWERLEVEL9K_BACKGROUND="#181825"
+ typeset -g POWERLEVEL9K_BACKGROUND="black"
typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR=''
typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR=''
@@ -93,11 +93,11 @@
typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL=
typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE=
- typeset -g POWERLEVEL9K_DIR_FOREGROUND="#89dceb"
+ typeset -g POWERLEVEL9K_DIR_FOREGROUND="blue"
typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique
typeset -g POWERLEVEL9K_SHORTEN_DELIMITER=
- typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND="#89dceb"
- typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND="#74c7ec"
+ typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND="blue"
+ typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND="blue"
typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true
local anchor_files=(
.bzr
@@ -544,7 +544,7 @@
typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68
- typeset -g POWERLEVEL9K_TIME_FOREGROUND="#cdd6f4"
+ typeset -g POWERLEVEL9K_TIME_FOREGROUND="white"
typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
typeset -g POWERLEVEL9K_TIME_PREFIX='%F{#bac2de}at '
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/assets/gpu-symbolic.svg b/hosts/totsugeki/home-manager/desktop/ags/config/assets/gpu-symbolic.svg
new file mode 100644
index 0000000..52c38c4
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/assets/gpu-symbolic.svg
@@ -0,0 +1,91 @@
+
+
+
+
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/assets/ram-custom-symbolic.svg b/hosts/totsugeki/home-manager/desktop/ags/config/assets/ram-custom-symbolic.svg
index 4c186ff..b7c40d1 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/config/assets/ram-custom-symbolic.svg
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/assets/ram-custom-symbolic.svg
@@ -1,10 +1,12 @@
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/config.js b/hosts/totsugeki/home-manager/desktop/ags/config/config.js
index 2ce3452..f06b4cc 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/config/config.js
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/config.js
@@ -1,13 +1,17 @@
-import cpu from "./services/cpu.js";
-import ram from "./services/ram.js";
+import { ProfilePicture } from "./widgets/bar/profile-picture.js";
+import {
+ ProcessorUsage,
+ MemoryUsage,
+ graphics_card_usage,
+ volume_widget,
+} from "./widgets/bar/system-stats.js";
+import { extended_bar } from "./widgets/bar-extended/extended-bar.js";
import { launcher } from "./widgets/launcher/launcher.js";
-const { speaker } = await Service.import("audio");
+import { popup_clock } from "./widgets/popup-clock/popup-clock.js";
+import { date } from "./state.js";
const mpris = await Service.import("mpris");
const sway = await Service.import("sway");
const players = mpris.bind("players");
-const date = Variable("", {
- poll: [1000, 'date "+%H %M %S %b %e"'],
-});
App.addIcons(`${App.configDir}/assets`);
@@ -43,67 +47,6 @@ function Workspaces() {
});
}
-function ProcessorUsage() {
- return Widget.Box({
- class_name: "system-stats",
- css: cpu.bind("current-usage").as((usage) => {
- let level = (usage * 100).toFixed(0);
- return `
- background: linear-gradient(
- 90deg, rgba(26, 27, 38, 0.6) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
- )`;
- }),
- children: [
- Widget.Icon({
- class_names: ["system-stats-icon"],
- icon: "microchip-symbolic",
- size: 16,
- }),
- Widget.Label({
- class_names: ["system-stats-text"],
- hexpand: true,
- label: cpu.bind("current-usage").as((usage) => {
- return `${(usage * 100).toFixed(0)}%`;
- }),
- }),
- ],
- vertical: true,
- });
-}
-
-function MemoryUsage() {
- return Widget.Box({
- class_name: "system-stats",
- css: ram.bind("current-usage-percentage").as((usage) => {
- let level = (usage * 100).toFixed(0);
- return `
- background: linear-gradient(
- 90deg, rgba(26, 27, 38, 0.6) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
- )`;
- }),
- children: [
- Widget.Icon({
- class_names: ["system-stats-icon"],
- icon: "ram-custom-symbolic",
- size: 16,
- }),
- Widget.Label({
- class_names: ["system-stats-text"],
- hexpand: true,
- label: ram.bind("current-usage-percentage").as((usage) => {
- return `${(usage * 100).toFixed(0)}%`;
- }),
- }),
- ],
- tooltip_text: ram.bind("current-usage").as((usage) => {
- let usageGb = (usage / 1024 ** 2).toFixed(2);
- let totalGb = (ram.total_available / 1024 ** 2).toFixed(2);
- return `${usageGb}/${totalGb}GB (${(ram.current_usage_percentage * 100).toFixed(2)}%)`;
- }),
- vertical: true,
- });
-}
-
function CurrentSong(player) {
return Widget.Box({
class_name: "music-indicator",
@@ -155,57 +98,6 @@ function CTest() {
});
}
-function ProfilePicture() {
- return Widget.Box({
- class_name: "profile-picture",
- css: "background-image: url('/home/avery/.face.icon'); background-size: cover",
- });
-}
-
-function VolumeWidget() {
- return Widget.Box({
- class_name: "volume-widget",
- children: [
- Widget.Icon({
- icon: speaker.bind("volume").as((l) => {
- let level = l.toFixed(2) * 100;
- if (level == 0) {
- return "audio-volume-muted";
- } else if (level > 0 && level <= 30) {
- return "audio-volume-low";
- } else if (level > 30 && level <= 70) {
- return "audio-volume-medium";
- } else {
- return "audio-volume-high";
- }
- }),
- size: 10,
- }),
- Widget.Box({
- class_name: "volume-box",
- css: speaker.bind("volume").as((l) => {
- let level = l.toFixed(2) * 100;
- if (level >= 0 && level <= 100) {
- return `
- background: linear-gradient(
- 90deg, #7aa2f7 ${level}%, #24283b ${level}%
- );
- `;
- } else if (level > 100 && level <= 200) {
- return `
- background: linear-gradient(
- 90deg, #f7768e ${level - 100}%, #7aa2f7 ${level - 100}%
- );
- `;
- } else {
- return "background: #f7768e";
- }
- }),
- hpack: "fill",
- }),
- ],
- });
-}
function ClockWidget() {
return Widget.Box({
children: [
@@ -239,11 +131,14 @@ const barContainer = Widget.Box({
spacing: 6,
vertical: true,
children: [
+ ProfilePicture(),
Workspaces(),
Widget.Box({ expand: true, vpack: "fill" }), // Separator
CTest(),
ProcessorUsage(),
MemoryUsage(),
+ graphics_card_usage,
+ volume_widget,
ClockWidget(),
],
});
@@ -252,6 +147,7 @@ const bar = Widget.Window({
name: "bar",
anchor: ["left", "top", "bottom"],
exclusivity: "exclusive",
+ margins: [4, 0, 4, 4],
child: barContainer,
});
@@ -266,5 +162,5 @@ Utils.monitorFile(
App.config({
style: "./style.css",
- windows: [bar, launcher],
+ windows: [bar, extended_bar, launcher, popup_clock],
});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/services/cpu.js b/hosts/totsugeki/home-manager/desktop/ags/config/services/cpu.js
index 70de005..e4a4e2a 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/config/services/cpu.js
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/services/cpu.js
@@ -1,16 +1,25 @@
class CPUService extends Service {
static {
- Service.register(this, {}, { "current-usage": ["float", "r"] });
+ Service.register(
+ this,
+ {},
+ { "current-usage": ["float", "r"], temperature: ["float", "r"] },
+ );
}
#previousIdle = 0.0;
#previousTotal = 0.0;
#currentUsage = 0.0;
+ #temperature = 0.0;
get current_usage() {
return this.#currentUsage;
}
+ get temperature() {
+ return this.#temperature;
+ }
+
constructor() {
super();
this.#update();
@@ -20,6 +29,10 @@ class CPUService extends Service {
}
#update() {
+ this.#temperature =
+ Utils.exec(
+ "sh -c 'cat /sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon1/temp1_input'",
+ ) / 1000;
const currentValues = Utils.exec(
"sh -c 'cat /proc/stat | grep cpu | head -n 1 | tr -s \" \"'",
)
@@ -33,6 +46,7 @@ class CPUService extends Service {
this.#previousIdle = idle;
this.#previousTotal = total;
this.changed("current-usage");
+ this.changed("temperature");
}
}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/services/gpu.js b/hosts/totsugeki/home-manager/desktop/ags/config/services/gpu.js
new file mode 100644
index 0000000..48b0162
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/services/gpu.js
@@ -0,0 +1,43 @@
+class GPUService extends Service {
+ static {
+ Service.register(
+ this,
+ {},
+ { "current-usage": ["float", "r"], temperature: ["float", "r"] },
+ );
+ }
+
+ #currentUsage = 0;
+ #temperature = 0;
+
+ get current_usage() {
+ return this.#currentUsage;
+ }
+
+ get temperature() {
+ return this.#temperature;
+ }
+
+ constructor() {
+ super();
+ this.#update();
+ const interval = setInterval(() => {
+ this.#update();
+ }, 2000);
+ }
+
+ #update() {
+ this.#currentUsage = Utils.exec(
+ "sh -c 'cat /sys/class/drm/card?/device/gpu_busy_percent'",
+ );
+ this.#temperature =
+ Utils.exec(
+ "sh -c 'cat /sys/class/drm/card?/device/hwmon/hwmon?/temp1_input'",
+ ) / 1000;
+ this.changed("current-usage");
+ this.changed("temperature");
+ }
+}
+
+const gpu = new GPUService();
+export default gpu;
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/state.js b/hosts/totsugeki/home-manager/desktop/ags/config/state.js
new file mode 100644
index 0000000..cdac73e
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/state.js
@@ -0,0 +1,55 @@
+export const date = Variable("", {
+ poll: [1000, 'date "+%H %M %S %Y-%m-%d"'],
+});
+
+class User {
+ constructor(user_string) {
+ var split_string = user_string.split(":");
+ this.username = split_string[0];
+ this.uid = split_string[2];
+ this.gid = split_string[3];
+ this.real_name = split_string[4];
+ if (this.real_name == "") {
+ this.real_name = this.username;
+ }
+ this.home_directory = split_string[5];
+ }
+}
+
+var is_bar_extended = false;
+var current_menu = "bar";
+
+// TODO(maybe): watch for changes to real name
+var uid = Utils.exec("sh -c 'echo $UID'");
+export const CURRENT_USER = new User(
+ Utils.exec(`grep -P '\\w+:x:${uid}' /etc/passwd`),
+);
+export const HOSTNAME = Utils.exec("hostname");
+
+export function on_window_event(_, window_name, visible) {
+ console.log(`is ${window_name} visible => ${visible}`);
+ console.log(`current menu is ${current_menu}`);
+ console.log(`is bar extended => ${is_bar_extended}`);
+ if (window_name != "bar" && visible) {
+ var previous_menu = current_menu;
+ current_menu = window_name;
+ if (previous_menu != "bar") {
+ App.getWindow(previous_menu).visible = false;
+ }
+ App.getWindow("bar").css = "opacity: 0";
+ if (window_name == "bar_extended") {
+ is_bar_extended = true;
+ }
+ } else if (window_name != "bar" && !visible) {
+ if (
+ (window_name == "bar_extended" && current_menu == "bar_extended") ||
+ !is_bar_extended
+ ) {
+ is_bar_extended = false;
+ current_menu = "bar";
+ App.getWindow("bar").css = "opacity: 1";
+ } else if (window_name != "bar_extended" && is_bar_extended) {
+ App.getWindow("bar_extended").visible = true;
+ }
+ }
+}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/style.css b/hosts/totsugeki/home-manager/desktop/ags/config/style.css
index 4d27b99..623b2f0 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/config/style.css
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/style.css
@@ -11,11 +11,15 @@ window {
.bar {
background-color: @bg;
border-radius: 8px;
- margin: 4px 0px 4px 4px;
padding: 4px 4px;
min-width: 32px;
}
+.extended_bar,
+.launcher {
+ min-width: 360px;
+}
+
.volume-widget {
margin: 4px;
}
@@ -30,7 +34,7 @@ window {
border-radius: 8px;
background-color: @bg;
font-weight: bold;
- font-family: "Iosevka Nerd Font";
+ font-family: monospace;
font-size: 1.2em;
padding: 4px 0;
}
@@ -67,8 +71,8 @@ window {
background-color: @bg;
border-radius: 8px;
color: white;
- font-family: "Iosevka Nerd Font";
- font-weight: bold;
+ font-family: monospace;
+ font-weight: 700;
font-size: 0.95em;
padding: 4px 0;
}
@@ -77,7 +81,7 @@ window {
background-color: @bg;
border-radius: 8px;
color: #c0caf5;
- font-family: "Iosevka Nerd Font";
+ font-family: monospace;
font-weight: bold;
font-size: 1em;
min-height: 20px;
@@ -89,13 +93,11 @@ window {
}
.occupied {
- border-radius: 8px;
min-width: 24px;
}
.active {
color: @bg-noa;
- border-radius: 8px;
min-width: 32px;
}
@@ -158,7 +160,6 @@ window {
.launcher-search:focus {
border: 2px solid white;
-
outline: none;
}
@@ -180,6 +181,117 @@ window {
}
.application_name {
- font-size: 1.4em;
+ font-size: 1.2em;
font-weight: 500;
}
+
+/*
+* Extended bar
+*/
+
+.profile_block {
+ background-color: @bg;
+ background-blend-mode: darken;
+ border-radius: 8px;
+ color: white;
+ padding: 4px;
+}
+
+.username {
+ font-weight: 600;
+ font-size: 1.4em;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.6);
+}
+
+.hostname {
+ font-weight: 600;
+ font-size: 1.1em;
+ color: rgba(255, 255, 255, 0.6);
+ font-family: monospace;
+}
+
+.workspace-button-big {
+ color: white;
+ font-family: "sans-serif";
+ font-weight: 400;
+ padding: 4px 8px;
+}
+
+.active {
+ color: @bg-noa;
+}
+
+.workspace-name {
+ font-weight: 500;
+ font-size: 1.2em;
+}
+
+.clock-big {
+ font-size: 1.4em;
+ color: white;
+}
+
+.system-stats-big {
+ padding: 4px 8px;
+}
+
+.system-stats-title {
+ font-family: sans-serif;
+ font-size: 1.2em;
+ font-weight: 500;
+}
+
+.currently-playing-big {
+ background-color: @bg;
+ border-radius: 8px;
+ padding: 4px;
+}
+
+.album-art-big {
+ border-radius: 8px;
+ min-width: 72px;
+ min-height: 72px;
+}
+
+.currently-playing-title {
+ color: white;
+ font-size: 1.2em;
+ font-weight: 700;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
+}
+
+.currently-playing-artists {
+ color: white;
+ font-weight: 600;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
+}
+
+calendar {
+ background-color: @bg;
+ border: none;
+ border-radius: 8px;
+ color: rgb(220, 220, 220);
+ font-family: sans-serif;
+ font-weight: 500;
+ padding: 1px;
+}
+
+calendar .header,
+calendar .highlight {
+ background-color: @normal;
+ color: white;
+}
+
+calendar .button {
+ background-color: transparent;
+ color: white;
+}
+
+calendar:selected {
+ background-color: rgba(255, 255, 255, 0.8);
+ color: black;
+}
+
+calendar:indeterminate {
+ color: rgb(90, 90, 90);
+}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/big-clock.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/big-clock.js
new file mode 100644
index 0000000..c20fa84
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/big-clock.js
@@ -0,0 +1,41 @@
+import { date } from "../../state.js";
+
+function BlinkingDots() {
+ return Widget.Label({
+ class_name: "blinking-dots",
+ label: ":",
+ });
+}
+
+export function Clock() {
+ return Widget.Box({
+ children: [
+ Widget.Box({ hexpand: true, hpack: "fill" }), // Separator
+ Widget.Box({
+ children: [
+ Widget.Label({
+ label: date.bind().as((d) => {
+ return d.split(" ")[0];
+ }),
+ }),
+ BlinkingDots(),
+ Widget.Label({
+ label: date.bind().as((d) => {
+ return d.split(" ")[1];
+ }),
+ }),
+ BlinkingDots(),
+ Widget.Label({
+ label: date.bind().as((d) => {
+ return d.split(" ")[2];
+ }),
+ }),
+ ],
+ }),
+ Widget.Box({ hexpand: true, hpack: "fill" }), // Separator
+ ],
+ class_names: ["clock", "clock-big"],
+ hpack: "fill",
+ spacing: 8,
+ });
+}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/currently-playing.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/currently-playing.js
new file mode 100644
index 0000000..e7d29d6
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/currently-playing.js
@@ -0,0 +1,78 @@
+const mpris = await Service.import("mpris");
+const players = mpris.bind("players");
+
+// TODO: rework
+export const currently_playing_controller = Widget.Box({
+ vertical: true,
+ visible: players.as((p) => p.length > 0),
+ children: players.as((p) => p.map(CurrentlyPlaying)),
+});
+
+function CurrentlyPlaying(player) {
+ return Widget.Box({
+ class_name: "currently-playing-big",
+ css: player.bind("cover_path").as((url) => {
+ if (url == undefined) {
+ return "";
+ }
+ console.log(url);
+ return `
+ background-image: url('${url}');
+ background-blend-mode: darken;
+ background-position: center;
+ background-size: cover;
+ `;
+ }),
+ spacing: 4,
+ children: [
+ Widget.Box({
+ class_name: "album-art-big",
+ css: player.bind("cover_path").as((url) => {
+ if (url == undefined) {
+ return `
+ background-color: rgba(0, 0, 0, 0.2);
+ background-image: url('${App.configDir}/assets/music-symbolic.svg');
+ background-size: 50% 50%;
+ background-repeat: no-repeat;
+ background-position: center;
+ `;
+ }
+ return `background-image: url('${url}'); background-size: cover;`;
+ }),
+ }),
+ Widget.Box({
+ children: [
+ Widget.Label({
+ class_name: "currently-playing-title",
+ label: player.bind("track-title"),
+ xalign: 0.5,
+ hexpand: true,
+ truncate: "end",
+ }),
+ Widget.Label({
+ class_name: "currently-playing-artists",
+ label: player.bind("track-artists").as((artists) => {
+ return artists.join(", ");
+ }),
+ xalign: 0.5,
+ hexpand: true,
+ truncate: "end",
+ }),
+ ],
+ vpack: "center",
+ vertical: true,
+ }),
+ // Widget.Icon({
+ // class_name: "playback-status",
+ // icon: player.bind("play-back-status").as((status) => {
+ // if (status == "Playing") {
+ // return "media-playback-start";
+ // } else if (status == "Paused") {
+ // return "media-playback-pause";
+ // }
+ // return "";
+ // }),
+ // }),
+ ],
+ });
+}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/extended-bar.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/extended-bar.js
new file mode 100644
index 0000000..1cf1e38
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/extended-bar.js
@@ -0,0 +1,45 @@
+import { Clock } from "./big-clock.js";
+import { currently_playing_controller } from "./currently-playing.js";
+import { ProfileBlock } from "./profile-block.js";
+import {
+ graphics_card_usage,
+ memory_usage,
+ processor_usage,
+ volume_widget,
+} from "./system-stats-big.js";
+import { workspaces } from "./workspaces.js";
+import { on_window_event } from "../../state.js";
+
+export const extended_bar = Widget.Window({
+ name: "bar_extended",
+ anchor: ["left", "top", "bottom"],
+ margins: [4, 0, 4, 4],
+ child: Widget.Box({
+ class_names: ["bar", "extended_bar"],
+ vpack: "fill",
+ spacing: 6,
+ vertical: true,
+ children: [
+ ProfileBlock(),
+ workspaces,
+ Widget.Box({ expand: true, vpack: "fill" }), // Separator
+ currently_playing_controller,
+ processor_usage,
+ memory_usage,
+ graphics_card_usage,
+ volume_widget,
+ Widget.Calendar({}),
+ Clock(),
+ ],
+ }),
+ exclusivity: "ignore",
+ visible: false,
+ layer: "overlay",
+ setup: (self) => {
+ self.hook(App, (_, window_name, visible) => {
+ if (window_name == self.name) {
+ on_window_event(_, window_name, visible);
+ }
+ });
+ },
+});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/profile-block.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/profile-block.js
new file mode 100644
index 0000000..46954b6
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/profile-block.js
@@ -0,0 +1,54 @@
+import { ProfilePicture } from "../bar/profile-picture.js";
+import { CURRENT_USER, HOSTNAME } from "../../state.js";
+
+const CURRENT_WALLPAPER_PATH =
+ "/home/avery/.local/share/wallpapers/.current_path";
+
+const wallpaperPath = Variable(Utils.readFile(CURRENT_WALLPAPER_PATH));
+
+const wallpaperMonitor = Utils.monitorFile(
+ CURRENT_WALLPAPER_PATH,
+ (file, event) => {
+ if (event === 1) {
+ wallpaperPath.value = Utils.readFile(file);
+ }
+ },
+);
+
+export function ProfileBlock() {
+ return Widget.Box({
+ class_name: "profile_block",
+ css: wallpaperPath.bind().as((path) => {
+ path =
+ path
+ .replace("\n", "")
+ .replace("/", "\\/")
+ .replace("wallpapers", "wallpaper_thumbnails") + ".jpg";
+ return `
+ background-image: url('${path}');
+ background-size: cover;
+ background-position: center;
+ `;
+ }),
+ spacing: 8,
+ children: [
+ ProfilePicture(40),
+ Widget.Box({
+ vertical: true,
+ vpack: "center",
+ children: [
+ Widget.Label({
+ class_name: "username",
+ label: CURRENT_USER.real_name,
+ xalign: 0,
+ }),
+ Widget.Label({
+ class_name: "hostname",
+ label: HOSTNAME,
+ xalign: 0,
+ }),
+ ],
+ }),
+ ],
+ });
+}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/system-stats-big.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/system-stats-big.js
new file mode 100644
index 0000000..1d3dc95
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/system-stats-big.js
@@ -0,0 +1,155 @@
+import cpu from "../../services/cpu.js";
+import ram from "../../services/ram.js";
+import gpu from "../../services/gpu.js";
+const { speaker } = await Service.import("audio");
+
+const PROCESSOR_NAME = Utils.exec(
+ 'bash -c \'cat /proc/cpuinfo | grep "model name" | head -n 1 | cut -d ":" -f 2 | cut -d " " -f 3,4,5\'',
+);
+const GRAPHICS_CARD_NAME = "Radeon RX 6700 XT";
+
+export const processor_usage = Widget.Box({
+ class_names: ["system-stats", "system-stats-big"],
+ css: cpu.bind("current-usage").as((usage) => {
+ let level = (usage * 100).toFixed(0);
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ )`;
+ }),
+ spacing: 6,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: "microchip-symbolic",
+ size: 16,
+ }),
+ Widget.Label({
+ class_name: "system-stats-title",
+ label: PROCESSOR_NAME,
+ }),
+ Widget.Label({
+ class_name: "system-stats-text-big",
+ hexpand: true,
+ xalign: 1,
+ label: cpu.bind("current-usage").as((usage) => {
+ return `${(usage * 100).toFixed(0)}% (${cpu.temperature.toFixed(0)}ºC)`;
+ }),
+ }),
+ ],
+});
+
+export const memory_usage = Widget.Box({
+ class_names: ["system-stats", "system-stats-big"],
+ css: ram.bind("current-usage-percentage").as((usage) => {
+ let level = (usage * 100).toFixed(0);
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ )`;
+ }),
+ spacing: 6,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: "ram-custom-symbolic",
+ size: 16,
+ }),
+ Widget.Label({
+ class_name: "system-stats-title",
+ label: "Memoria",
+ }),
+ Widget.Label({
+ class_name: "system-stats-text-big",
+ hexpand: true,
+ xalign: 1,
+ label: ram.bind("current-usage").as((usage) => {
+ let usageGb = (usage / 1024 ** 2).toFixed(2);
+ let totalGb = (ram.total_available / 1024 ** 2).toFixed(2);
+ return `${usageGb}/${totalGb}GiB (${(ram.current_usage_percentage * 100).toFixed(2)}%)`;
+ }),
+ }),
+ ],
+});
+
+export const graphics_card_usage = Widget.Box({
+ class_names: ["system-stats", "system-stats-big"],
+ css: gpu.bind("current-usage").as((usage) => {
+ var level = Number(usage).toFixed(0);
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ )`;
+ }),
+ spacing: 6,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: "gpu-symbolic",
+ size: 16,
+ }),
+ Widget.Label({
+ class_name: "system-stats-title",
+ label: GRAPHICS_CARD_NAME,
+ }),
+ Widget.Label({
+ class_name: "system-stats-text-big",
+ hexpand: true,
+ xalign: 1,
+ label: gpu.bind("current-usage").as((usage) => {
+ return `${Number(usage).toFixed(0)}% (${gpu.temperature.toFixed(0)}ºC)`;
+ }),
+ }),
+ ],
+});
+
+export const volume_widget = Widget.Box({
+ class_names: ["system-stats", "system-stats-big"],
+ css: speaker.bind("volume").as((l) => {
+ let level = l.toFixed(2) * 100;
+ if (level >= 0 && level <= 100) {
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ );`;
+ } else if (level > 100 && level <= 200) {
+ return `
+ background: linear-gradient(
+ 90deg, rgba(226, 27, 38, 0.4) ${level - 100}%, rgba(26, 27, 38, 0.8) ${level - 100}%
+ );`;
+ } else {
+ return "background: rgba(226, 27, 38, 0.4)";
+ }
+ }),
+ spacing: 6,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: speaker.bind("volume").as((l) => {
+ let level = l.toFixed(2) * 100;
+ if (level == 0) {
+ return "audio-volume-muted";
+ } else if (level > 0 && level <= 30) {
+ return "audio-volume-low";
+ } else if (level > 30 && level <= 70) {
+ return "audio-volume-medium";
+ } else {
+ return "audio-volume-high";
+ }
+ }),
+ size: 16,
+ }),
+ Widget.Label({
+ class_name: "system-stats-title",
+ label: "Volumen",
+ }),
+ Widget.Label({
+ class_name: "system-stats-text-big",
+ hexpand: true,
+ xalign: 1,
+ label: speaker.bind("volume").as((l) => {
+ return `${(l * 100).toFixed(0)}%`;
+ }),
+ }),
+ ],
+});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/workspaces.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/workspaces.js
new file mode 100644
index 0000000..d4737b6
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar-extended/workspaces.js
@@ -0,0 +1,81 @@
+const sway = await Service.import("sway");
+
+function getWorkspaceWindows(node) {
+ var nodes = [];
+ node.nodes.concat(node.floating_nodes).forEach((child_node) => {
+ if (
+ child_node.app_id != undefined ||
+ child_node.window_properties != undefined
+ ) {
+ nodes.push(child_node);
+ } else {
+ nodes = nodes.concat(getWorkspaceWindows(child_node));
+ }
+ });
+ return nodes;
+}
+
+export const workspaces = Widget.Box({
+ children: Array.from({ length: 10 }, (_, i) => {
+ i += 1;
+ return Widget.Box({
+ class_names: ["workspace-button", "workspace-button-big"],
+ spacing: 16,
+ children: [
+ Widget.Label({
+ class_name: "workspace-name",
+ label: `Escritorio ${i.toString()}`,
+ }),
+ Widget.Box({
+ children: [
+ Widget.Label({
+ label: "dummy",
+ hexpand: true,
+ xalign: 1,
+ truncate: "end",
+ setup: (label) => {
+ label.hook(sway, (label) => {
+ const ws = sway.getWorkspace(`${i}`);
+ if (ws === undefined) {
+ label.visible = false;
+ return;
+ }
+ var nodes = getWorkspaceWindows(ws);
+ if (nodes.length == 1) {
+ label.label = nodes[0].name;
+ label.visible = true;
+ } else if (nodes.length > 0) {
+ label.label = `${nodes.length} ventanas abiertas`;
+ label.visible = true;
+ } else {
+ label.label = "No hay ventanas abiertas";
+ label.visible = false;
+ }
+ });
+ },
+ }),
+ ],
+ }),
+ ],
+ setup: (btn) => {
+ btn.hook(
+ sway,
+ (btn) => {
+ const ws = sway.getWorkspace(`${i}`);
+ btn.toggleClassName(
+ "occupied",
+ ws?.nodes.length + ws?.floating_nodes.length > 0,
+ );
+ },
+ "notify::workspaces",
+ );
+
+ btn.hook(sway.active.workspace, (btn) => {
+ btn.toggleClassName("active", sway.active.workspace.name == i);
+ });
+ },
+ });
+ }),
+ spacing: 4,
+ vertical: true,
+});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar/profile-picture.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar/profile-picture.js
new file mode 100644
index 0000000..04e5b1b
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar/profile-picture.js
@@ -0,0 +1,12 @@
+// TODO: user-agnostic, reload if changed
+export function ProfilePicture(size = 32) {
+ return Widget.Box({
+ class_name: "profile-picture",
+ css: `
+ background-image: url('/home/avery/.face.icon');
+ background-size: cover;
+ min-width: ${size}px;
+ min-height: ${size}px;
+ `,
+ });
+}
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar/system-stats.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar/system-stats.js
new file mode 100644
index 0000000..d12b389
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/bar/system-stats.js
@@ -0,0 +1,141 @@
+import cpu from "../../services/cpu.js";
+import gpu from "../../services/gpu.js";
+import ram from "../../services/ram.js";
+const { speaker } = await Service.import("audio");
+
+export function ProcessorUsage() {
+ return Widget.Box({
+ class_name: "system-stats",
+ css: cpu.bind("current-usage").as((usage) => {
+ let level = (usage * 100).toFixed(0);
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ )`;
+ }),
+ spacing: 2,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: "microchip-symbolic",
+ size: 16,
+ }),
+ Widget.Label({
+ class_names: ["system-stats-text"],
+ hexpand: true,
+ label: cpu.bind("current-usage").as((usage) => {
+ return `${(usage * 100).toFixed(0)}%`;
+ }),
+ }),
+ ],
+ vertical: true,
+ });
+}
+
+export function MemoryUsage() {
+ return Widget.Box({
+ class_name: "system-stats",
+ css: ram.bind("current-usage-percentage").as((usage) => {
+ let level = (usage * 100).toFixed(0);
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ )`;
+ }),
+ spacing: 2,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: "ram-custom-symbolic",
+ size: 16,
+ }),
+ Widget.Label({
+ class_names: ["system-stats-text"],
+ hexpand: true,
+ label: ram.bind("current-usage-percentage").as((usage) => {
+ return `${(usage * 100).toFixed(0)}%`;
+ }),
+ }),
+ ],
+ tooltip_text: ram.bind("current-usage").as((usage) => {
+ let usageGb = (usage / 1024 ** 2).toFixed(2);
+ let totalGb = (ram.total_available / 1024 ** 2).toFixed(2);
+ return `${usageGb}/${totalGb}GiB (${(ram.current_usage_percentage * 100).toFixed(2)}%)`;
+ }),
+ vertical: true,
+ });
+}
+
+export const graphics_card_usage = Widget.Box({
+ class_name: "system-stats",
+ css: gpu.bind("current-usage").as((usage) => {
+ var level = Number(usage).toFixed(0);
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ )`;
+ }),
+ spacing: 2,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: "gpu-symbolic",
+ size: 16,
+ }),
+ Widget.Label({
+ class_names: ["system-stats-text"],
+ hexpand: true,
+ label: gpu.bind("current-usage").as((usage) => {
+ return `${Number(usage).toFixed(0)}%`;
+ }),
+ }),
+ ],
+ vertical: true,
+});
+
+export const volume_widget = Widget.Box({
+ class_name: "system-stats",
+ css: speaker.bind("volume").as((l) => {
+ let level = l.toFixed(2) * 100;
+ if (level >= 0 && level <= 100) {
+ return `
+ background: linear-gradient(
+ 90deg, rgba(26, 27, 38, 0.8) ${level}%, rgba(26, 27, 38, 0.4) ${level}%
+ );`;
+ } else if (level > 100 && level <= 200) {
+ return `
+ background: linear-gradient(
+ 90deg, rgba(226, 27, 38, 0.4) ${level - 100}%, rgba(26, 27, 38, 0.8) ${level - 100}%
+ );`;
+ } else {
+ return "background: rgba(226, 27, 38, 0.4)";
+ }
+ }),
+ spacing: 2,
+ vertical: true,
+ children: [
+ Widget.Icon({
+ class_names: ["system-stats-icon"],
+ icon: speaker.bind("volume").as((l) => {
+ let level = l.toFixed(2) * 100;
+ if (level == 0) {
+ return "audio-volume-muted";
+ } else if (level > 0 && level <= 30) {
+ return "audio-volume-low";
+ } else if (level > 30 && level <= 70) {
+ return "audio-volume-medium";
+ } else {
+ return "audio-volume-high";
+ }
+ }),
+ size: 16,
+ }),
+ Widget.Label({
+ class_name: "system-stats-text",
+ hexpand: true,
+ label: speaker.bind("volume").as((l) => {
+ return `${(l * 100).toFixed(0)}%`;
+ }),
+ }),
+ ],
+});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/application.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/application.js
index c855ae4..cef3ee3 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/application.js
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/application.js
@@ -10,7 +10,7 @@ export const Application = (application) =>
children: [
Widget.Icon({
icon: application.icon_name || "",
- size: 32,
+ size: 24,
}),
Widget.Label({
class_name: "application_name",
@@ -20,6 +20,6 @@ export const Application = (application) =>
truncate: "end",
}),
],
- spacing: 8,
+ spacing: 4,
}),
});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/launcher.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/launcher.js
index 80052bc..c34adb0 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/launcher.js
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/launcher/launcher.js
@@ -1,13 +1,14 @@
import { Application } from "./application.js";
+import { on_window_event } from "../../state.js";
const { query } = await Service.import("applications");
-const Launcher = ({ width = 500, height = 600, spacing = 4 }) => {
+const Launcher = () => {
let applications = query("").map(Application);
const list = Widget.Box({
css: "background-color: transparent",
vertical: true,
- spacing,
+ spacing: 4,
children: applications,
});
@@ -54,20 +55,20 @@ const Launcher = ({ width = 500, height = 600, spacing = 4 }) => {
return Widget.Box({
class_name: "launcher",
vertical: true,
- spacing,
+ spacing: 4,
children: [
searchBox,
Widget.Scrollable({
hscroll: "never",
- css: `min-width: ${width}px; min-height: ${height}px;`,
child: list,
vexpand: true,
}),
],
setup: (self) =>
- self.hook(App, (_, windowName, visible) => {
- if (windowName == "launcher" && visible) {
+ self.hook(App, (_, window_name, visible) => {
+ if (window_name == "launcher" && visible) {
repopulate();
+ applications[0].grab_focus(); // Scrolls application list to top
searchBox.text = "";
searchBox.grab_focus();
}
@@ -77,11 +78,20 @@ const Launcher = ({ width = 500, height = 600, spacing = 4 }) => {
export const launcher = Widget.Window({
name: "launcher",
- setup: (self) =>
+ anchor: ["left", "top", "bottom"],
+ setup: (self) => {
+ self.hook(App, (_, window_name, visible) => {
+ if (window_name == self.name) {
+ on_window_event(_, window_name, visible);
+ }
+ });
self.keybind("Escape", () => {
App.closeWindow("launcher");
- }),
+ });
+ },
visible: false,
+ margins: [4, 0, 4, 4],
+ exclusivity: "ignore",
keymode: "exclusive",
child: Launcher({}),
});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/config/widgets/popup-clock/popup-clock.js b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/popup-clock/popup-clock.js
new file mode 100644
index 0000000..5bb7ec8
--- /dev/null
+++ b/hosts/totsugeki/home-manager/desktop/ags/config/widgets/popup-clock/popup-clock.js
@@ -0,0 +1,10 @@
+import { Clock } from "../bar-extended/big-clock.js";
+
+export const popup_clock = Widget.Window({
+ name: "popup_clock",
+ visible: false,
+ anchor: ["right", "top"],
+ margins: [16, 16, 0, 0],
+ layer: "overlay",
+ child: Clock(),
+});
diff --git a/hosts/totsugeki/home-manager/desktop/ags/default.nix b/hosts/totsugeki/home-manager/desktop/ags/default.nix
index 71e5b1d..28a8cfd 100644
--- a/hosts/totsugeki/home-manager/desktop/ags/default.nix
+++ b/hosts/totsugeki/home-manager/desktop/ags/default.nix
@@ -1,4 +1,5 @@
-{ ... }: {
+{ pkgs, ... }: {
+ home.packages = with pkgs; [ pciutils ];
programs.ags = {
enable = true;
# configDir = ./widgets;