diff --git a/astal/.gitignore b/astal/.gitignore new file mode 100644 index 0000000..298eb4d --- /dev/null +++ b/astal/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +@girs/ diff --git a/astal/app.ts b/astal/app.ts new file mode 100644 index 0000000..08a2f70 --- /dev/null +++ b/astal/app.ts @@ -0,0 +1,10 @@ +import { App } from "astal/gtk3"; +import style from "./style.scss"; +import Bar from "./widget/Bar"; + +App.start({ + css: style, + main() { + App.get_monitors().map(Bar); + }, +}); diff --git a/astal/env.d.ts b/astal/env.d.ts new file mode 100644 index 0000000..467c0a4 --- /dev/null +++ b/astal/env.d.ts @@ -0,0 +1,21 @@ +declare const SRC: string + +declare module "inline:*" { + const content: string + export default content +} + +declare module "*.scss" { + const content: string + export default content +} + +declare module "*.blp" { + const content: string + export default content +} + +declare module "*.css" { + const content: string + export default content +} diff --git a/astal/flake.lock b/astal/flake.lock new file mode 100644 index 0000000..562d628 --- /dev/null +++ b/astal/flake.lock @@ -0,0 +1,70 @@ +{ + "nodes": { + "ags": { + "inputs": { + "astal": "astal", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1744557573, + "narHash": "sha256-XAyj0iDuI51BytJ1PwN53uLpzTDdznPDQFG4RwihlTQ=", + "owner": "aylur", + "repo": "ags", + "rev": "3ed9737bdbc8fc7a7c7ceef2165c9109f336bff6", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "ags", + "type": "github" + } + }, + "astal": { + "inputs": { + "nixpkgs": [ + "ags", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1742571008, + "narHash": "sha256-5WgfJAeBpxiKbTR/gJvxrGYfqQRge5aUDcGKmU1YZ1Q=", + "owner": "aylur", + "repo": "astal", + "rev": "dc0e5d37abe9424c53dcbd2506a4886ffee6296e", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "astal", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1750365781, + "narHash": "sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "08f22084e6085d19bcfb4be30d1ca76ecb96fe54", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "ags": "ags", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/astal/flake.nix b/astal/flake.nix new file mode 100644 index 0000000..7554ded --- /dev/null +++ b/astal/flake.nix @@ -0,0 +1,62 @@ +{ + description = "Desktop widgets"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + + ags = { + url = "github:aylur/ags"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + ags, + }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + + extraPkgs = with ags.packages.${system}; [ + hyprland + battery + wireplumber + network + ]; + in + { + packages.${system} = { + status-bar = ags.lib.bundle { + inherit pkgs; + src = ./.; + name = "status-bar"; + entry = "app.ts"; + + extraPackages = + with pkgs; + extraPkgs + ++ [ + libgtop + ]; + }; + }; + + devShells.${system} = { + default = pkgs.mkShell { + buildInputs = [ + (ags.packages.${system}.default.override { + extraPackages = + with pkgs; + extraPkgs + ++ [ + libgtop + ]; + }) + ]; + }; + }; + }; +} diff --git a/astal/package-lock.json b/astal/package-lock.json new file mode 100644 index 0000000..635a9c5 --- /dev/null +++ b/astal/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "astal-shell", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "astal-shell", + "dependencies": { + "astal": "/nix/store/pvb3x021mr6xknm91gqq76gy32n96vj0-astal-gjs/share/astal/gjs" + } + }, + "../../../../nix/store/pvb3x021mr6xknm91gqq76gy32n96vj0-astal-gjs/share/astal/gjs": { + "name": "astal", + "license": "LGPL-2.1" + }, + "node_modules/astal": { + "resolved": "../../../../nix/store/pvb3x021mr6xknm91gqq76gy32n96vj0-astal-gjs/share/astal/gjs", + "link": true + } + } +} diff --git a/astal/package.json b/astal/package.json new file mode 100644 index 0000000..32f5d43 --- /dev/null +++ b/astal/package.json @@ -0,0 +1,6 @@ +{ + "name": "astal-shell", + "dependencies": { + "astal": "/nix/store/pvb3x021mr6xknm91gqq76gy32n96vj0-astal-gjs/share/astal/gjs" + } +} diff --git a/astal/style.scss b/astal/style.scss new file mode 100644 index 0000000..a1ad609 --- /dev/null +++ b/astal/style.scss @@ -0,0 +1,122 @@ +@use "sass:color"; + +$bg: #212223; +$fg: #f1f1f1; +$accent: #378DF7; +$accent-white: #ffffff; +$radius: 7px; +$bg-color-6: rgb(40, 42, 54); +$inactive-bg-color: rgb(68, 71, 90); + +window.Bar { + border: none; + box-shadow: none; + font-family: Dejavu Sans Mono; + background-color: color.adjust($bg, $alpha: -0.2); + color: $fg; + font-size: 1.1em; + + label { + margin: 0 8px; + } + + .status-box { + background-color: $bg-color-6; + padding: 0 4px; + margin: 0 4px; + font-size: 16px; + } + + .inactive { + background-color: $inactive-bg-color; + } + + .workspaces { + font-weight: bold; + + button { + all: unset; + background-color: transparent; + + &:hover label { + background-color: color.adjust($fg, $alpha: -0.84); + border-color: color.adjust($accent, $alpha: -0.8); + } + + &:active label { + background-color: color.adjust($fg, $alpha: -0.8) + } + } + + label { + transition: 200ms; + padding: 3px 8px; + margin: 0px; + border-radius: 0; + border: 3px solid transparent; + } + + .focused label { + background-color: color.adjust($accent-white, $alpha: -0.84); + border-bottom: 3px solid $accent-white; + } + } + + .SysTray { + margin-right: 8px; + + button { + padding: 0 4px; + } + } + + .FocusedClient { + color: $accent; + } + + .Media .Cover { + min-height: 1.2em; + min-width: 1.2em; + border-radius: $radius; + background-position: center; + background-size: contain; + } + + .Battery label { + padding-left: 0; + margin-left: 0; + } + + .AudioSlider { + * { + all: unset; + } + + icon { + margin-right: .6em; + } + + & { + margin: 0 1em; + } + + trough { + background-color: color.adjust($fg, $alpha: -0.8); + border-radius: $radius; + } + + highlight { + background-color: $accent; + min-height: .8em; + border-radius: $radius; + } + + slider { + background-color: $fg; + border-radius: $radius; + min-height: 1em; + min-width: 1em; + margin: -.2em; + } + } +} diff --git a/astal/tsconfig.json b/astal/tsconfig.json new file mode 100644 index 0000000..9471e35 --- /dev/null +++ b/astal/tsconfig.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "experimentalDecorators": true, + "strict": true, + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "Bundler", + // "checkJs": true, + // "allowJs": true, + "jsx": "react-jsx", + "jsxImportSource": "astal/gtk3", + } +} diff --git a/astal/widget/Bar.tsx b/astal/widget/Bar.tsx new file mode 100644 index 0000000..4b26138 --- /dev/null +++ b/astal/widget/Bar.tsx @@ -0,0 +1,156 @@ +import { App, Astal, Gtk, Gdk } from "astal/gtk3" +import { bind, derive, GLib, Variable } from "astal" +import Hyprland from "gi://AstalHyprland" +import Wp from "gi://AstalWp" +import Network from "gi://AstalNetwork" +import GTop from "gi://GTop" +import { calc_cpu_usage, get_cpu_snapshot, get_disk_space, get_ram_info } from "./cpu" +import AstalBattery from "gi://AstalBattery" + +const time = Variable("").poll(1000, "date") + +function Workspaces() { + const hypr = Hyprland.get_default() + + return + {bind(hypr, "workspaces").as(wss => wss.filter(ws => !(ws.id >= -99 && ws.id <= -2)) + .sort((a, b) => a.id - b.id) + .map(ws => ( + //@ts-ignore + + )))} + +} + +function Audio() { + const speaker = Wp.get_default()?.default_speaker! + const derived = Variable.derive([bind(speaker, "volume"), bind(speaker, "mute")], (volume: number, muted: boolean) => { + if (muted) { + return { label: ` (muted)`, muted } + } + return { label: `${Math.floor(volume * 100)}% ` } + }) + + return ["status-box", v.muted && "inactive"].filter(Boolean).join(" "))}> + +} + +function NetworkModule() { + const network = Network.get_default() + const wifi = network.wifi; + const wired = network.wired + + const derived = Variable.derive([bind(network, "primary"), bind(wifi, "ssid"), bind(wifi, "strength")], (primary, ssid, strength) => { + if (primary === Network.Primary.WIRED) { + return { label: "🖧 Wired" } + } + if (wifi.active_access_point !== null) { + return { label: `${ssid} (${strength}%) ` } + } + return { label: `Disconnected ⚠` } + }) + + return ( + + + ) +} + +let s1 = get_cpu_snapshot(); +let cpu_usage_percent = Variable(0); +GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => { + const s2 = get_cpu_snapshot(); + cpu_usage_percent.set(calc_cpu_usage(s1, s2)); + s1 = s2; + + return true; +}); +function Cpu() { + return + +} + +let info = Variable(get_ram_info()) +GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => { + info.set(get_ram_info()) + + return true +}) +function Ram() { + return + +} + +let disk_space = Variable(get_disk_space()) +function Disk() { + return + +} + +function Battery() { + const battery = AstalBattery.get_default() + const battery_info = Variable.derive([bind(battery, "percentage"), bind(battery, "charging")], (percentage, charging) => { + const full_percentage = Math.floor(percentage * 100) + if (charging) { + return { label: `${full_percentage == 100 ? "FULL" : "CHR"}: ${full_percentage}%` } + } + return { label: `${full_percentage == 100 ? "FULL" : "BAT"}: ${full_percentage}` } + }) + + return + +} + +let current_time = Variable(GLib.DateTime.new_now_local().format("%H:%M")) +GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => { + const now = GLib.DateTime.new_now_local() + current_time.set(now.format("%H:%M")) + return true +}) +function Time() { + + return + +} + +export default function Bar(gdkmonitor: Gdk.Monitor) { + const { BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor + + //@ts-ignore + return + + + + + + + + + +} diff --git a/astal/widget/cpu.ts b/astal/widget/cpu.ts new file mode 100644 index 0000000..0175b30 --- /dev/null +++ b/astal/widget/cpu.ts @@ -0,0 +1,46 @@ +import { GLib, Variable } from "astal"; +import GTop from "gi://GTop"; + +type Snapshot = { + total: number; + user: number; + sys: number; + idle: number; +}; + +export function get_cpu_snapshot() { + const cpu = new GTop.glibtop_cpu(); + GTop.glibtop_get_cpu(cpu); + return { + total: cpu.total, + user: cpu.user + cpu.nice, + sys: cpu.sys, + idle: cpu.idle, + }; +} + +export function calc_cpu_usage(a: Snapshot, b: Snapshot) { + const total_diff = b.total - a.total; + const active_diff = b.user + b.sys - (a.user + a.sys); + return Math.round(total_diff > 0 ? (100 * active_diff) / total_diff : 0); +} + +export function get_ram_info() { + const mem = new GTop.glibtop_mem(); + GTop.glibtop_get_mem(mem); + return { + total: mem.total, + used: mem.total - mem.free - mem.cached - mem.buffer, + free: mem.free, + }; +} + +export function get_disk_space() { + const usage = new GTop.glibtop_fsusage(); + GTop.glibtop_get_fsusage(usage, "/"); + + const free_bytes = usage.bavail * usage.block_size; + const free_gib = free_bytes / 1024 ** 3; + + return free_gib.toFixed(2); +} diff --git a/nix/flake.lock b/nix/flake.lock index 638e9be..624877f 100644 --- a/nix/flake.lock +++ b/nix/flake.lock @@ -1,5 +1,49 @@ { "nodes": { + "ags": { + "inputs": { + "astal": "astal", + "nixpkgs": [ + "status-bar", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1744557573, + "narHash": "sha256-XAyj0iDuI51BytJ1PwN53uLpzTDdznPDQFG4RwihlTQ=", + "owner": "aylur", + "repo": "ags", + "rev": "3ed9737bdbc8fc7a7c7ceef2165c9109f336bff6", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "ags", + "type": "github" + } + }, + "astal": { + "inputs": { + "nixpkgs": [ + "status-bar", + "ags", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1742571008, + "narHash": "sha256-5WgfJAeBpxiKbTR/gJvxrGYfqQRge5aUDcGKmU1YZ1Q=", + "owner": "aylur", + "repo": "astal", + "rev": "dc0e5d37abe9424c53dcbd2506a4886ffee6296e", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "astal", + "type": "github" + } + }, "custom-fonts": { "inputs": { "nixpkgs": [ @@ -39,11 +83,11 @@ ] }, "locked": { - "lastModified": 1750304462, - "narHash": "sha256-Mj5t4yX05/rXnRqJkpoLZTWqgStB88Mr/fegTRqyiWc=", + "lastModified": 1750650158, + "narHash": "sha256-cGdpDr/OUjS5rfl0lH7CjnIFWtpOFQOcRkvBu9CV+Cs=", "owner": "nix-community", "repo": "home-manager", - "rev": "863842639722dd12ae9e37ca83bcb61a63b36f6c", + "rev": "3bd646138a9c71f244a9293dde6f59ae1c6875ab", "type": "github" }, "original": { @@ -110,11 +154,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1750134718, - "narHash": "sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI=", + "lastModified": 1750506804, + "narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9e83b64f727c88a7711a2c463a7b16eedb69a84c", + "rev": "4206c4cb56751df534751b058295ea61357bbbaa", "type": "github" }, "original": { @@ -172,8 +216,26 @@ "nixos-wsl": "nixos-wsl", "nixpkgs": "nixpkgs_2", "pesde": "pesde", - "rokit": "rokit" + "rokit": "rokit", + "status-bar": "status-bar" } + }, + "status-bar": { + "inputs": { + "ags": "ags", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "path": "../astal", + "type": "path" + }, + "original": { + "path": "../astal", + "type": "path" + }, + "parent": [] } }, "root": "root", diff --git a/nix/flake.nix b/nix/flake.nix index 158cf61..05b1b6f 100644 --- a/nix/flake.nix +++ b/nix/flake.nix @@ -14,6 +14,10 @@ rokit.inputs.nixpkgs.follows = "nixpkgs"; custom-fonts.url = "path:./fonts"; custom-fonts.inputs.nixpkgs.follows = "nixpkgs"; + status-bar = { + url = "path:../astal"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = @@ -72,6 +76,7 @@ inputs.pesde.packages.${host.architecture}.default inputs.mantle.packages.${host.architecture}.default inputs.rokit.packages.${host.architecture}.default + inputs.status-bar.packages.${host.architecture}.status-bar ]; fonts.packages = [ inputs.custom-fonts.packages.${host.architecture}.default diff --git a/nix/modules/desktop.nix b/nix/modules/desktop.nix index 816ff2e..3764651 100644 --- a/nix/modules/desktop.nix +++ b/nix/modules/desktop.nix @@ -40,6 +40,7 @@ services.tumbler.enable = true; services.displayManager.ly.enable = true; rofi.enable = true; + services.upower.enable = true; home-manager.users.luca = { programs = { @@ -139,7 +140,7 @@ ]; exec-once = [ "swaybg -i ~/.config/wallpaper/bg.jpg" - "waybar" + "status-bar" ]; monitor = [ "eDP-1, 1920x1080, 0x0, 1"