From 16813aeef93b1d4feea0dd0f3838a6b42eb3b10e Mon Sep 17 00:00:00 2001 From: Nickolaj Jepsen Date: Fri, 31 Jan 2025 15:13:22 +0100 Subject: [PATCH] complete rework --- .gitignore | 1 + flake.lock | 559 +++--------------- flake.nix | 86 +-- home-manager/default.nix | 50 -- home-manager/modules/eww/config/eww.scss | 112 ---- home-manager/modules/eww/config/eww.yuck | 98 --- .../modules/eww/config/scripts/battery-level | 2 - .../modules/eww/config/scripts/battery-status | 2 - .../config/scripts/change-active-workspace | 21 - .../eww/config/scripts/get-active-monitor | 6 - .../eww/config/scripts/get-active-workspace | 6 - .../eww/config/scripts/get-window-title | 3 - .../modules/eww/config/scripts/get-workspaces | 10 - .../modules/eww/config/scripts/volume | 95 --- home-manager/modules/eww/default.nix | 15 - .../modules/fish/conf.d/10_theme.fish | 38 -- .../modules/fish/conf.d/20_alias.fish | 37 -- .../modules/fish/conf.d/30_kubectl.fish | 47 -- home-manager/modules/fish/default.nix | 42 -- home-manager/modules/nixvim/default.nix | 90 --- justfile | 30 + machines/desktop/default.nix | 15 + machines/legion/configuration.nix | 52 -- machines/legion/hardware-configuration.nix | 35 -- machines/legion/home-manager.nix | 152 ----- machines/legion/nvidia.nix | 23 - machines/qemu/default.nix | 18 + machines/virtualbox/configuration.nix | 58 -- machines/virtualbox/home-manager.nix | 31 - machines/wsl/configuration.nix | 26 - machines/wsl/home-manager.nix | 0 modules/astal/default.nix | 46 ++ modules/astal/src/.gitignore | 3 + modules/astal/src/app.ts | 11 + modules/astal/src/biome.json | 37 ++ modules/astal/src/env.d.ts | 21 + modules/astal/src/icons/README.md | 2 + .../src/icons/chrome-custom-symbolic.svg | 6 + .../src/icons/circle-filled-symbolic.svg | 3 + modules/astal/src/icons/circle-symbolic.svg | 3 + .../src/icons/firefox-custom-symbolic.svg | 4 + modules/astal/src/icons/git-symbolic.svg | 3 + .../src/icons/microphone-custom-symbolic.svg | 4 + modules/astal/src/icons/plus-symbolic.svg | 4 + modules/astal/src/icons/python-symbolic.svg | 4 + .../astal/src/icons/star-filled-symbolic.svg | 3 + modules/astal/src/icons/terminal-symbolic.svg | 4 + .../src/icons/vscode-custom-symbolic.svg | 5 + modules/astal/src/package-lock.json | 188 ++++++ modules/astal/src/package.json | 9 + modules/astal/src/src/bar/Bar.scss | 88 +++ modules/astal/src/src/bar/Bar.tsx | 122 ++++ modules/astal/src/src/bar/SecondaryBar.tsx | 106 ++++ .../astal/src/src/bar/sections/Dropdown.scss | 10 + .../astal/src/src/bar/sections/Dropdown.tsx | 133 +++++ modules/astal/src/src/bar/sections/Media.scss | 37 ++ modules/astal/src/src/bar/sections/Media.tsx | 387 ++++++++++++ .../astal/src/src/bar/sections/Playback.scss | 28 + .../astal/src/src/bar/sections/Playback.tsx | 250 ++++++++ .../astal/src/src/bar/sections/Workspace.scss | 62 ++ .../astal/src/src/bar/sections/Workspace.tsx | 102 ++++ modules/astal/src/src/config.ts | 11 + modules/astal/src/src/main.scss | 49 ++ modules/astal/src/src/main.ts | 32 + .../src/src/notification/Notification.scss | 47 ++ .../src/src/notification/Notification.tsx | 117 ++++ .../src/notification/NotificationPopups.tsx | 109 ++++ modules/astal/src/src/utils/gtk.ts | 15 + modules/astal/src/src/utils/io.ts | 59 ++ modules/astal/src/src/utils/monitors.ts | 52 ++ modules/astal/src/src/utils/timeout.ts | 24 + modules/astal/src/src/utils/var-map.ts | 55 ++ modules/astal/src/src/variables.scss | 13 + modules/astal/src/src/widgets.ts | 90 +++ modules/astal/src/tsconfig.json | 12 + modules/audio.nix | 11 + modules/base.nix | 4 + modules/btop.nix | 12 + modules/fonts.nix | 6 + modules/ghostty.nix | 15 + modules/greetd.nix | 13 + modules/hyprland/default.nix | 165 ++++++ modules/hyprland/hyprpolkitagent.nix | 25 + modules/monitors.nix | 57 ++ modules/user.nix | 34 ++ nixos/configuration.nix | 55 -- result | 1 + targets/graphical.nix | 12 + targets/shell.nix | 6 + 89 files changed, 2888 insertions(+), 1658 deletions(-) create mode 100644 .gitignore delete mode 100644 home-manager/default.nix delete mode 100644 home-manager/modules/eww/config/eww.scss delete mode 100644 home-manager/modules/eww/config/eww.yuck delete mode 100644 home-manager/modules/eww/config/scripts/battery-level delete mode 100644 home-manager/modules/eww/config/scripts/battery-status delete mode 100755 home-manager/modules/eww/config/scripts/change-active-workspace delete mode 100755 home-manager/modules/eww/config/scripts/get-active-monitor delete mode 100755 home-manager/modules/eww/config/scripts/get-active-workspace delete mode 100755 home-manager/modules/eww/config/scripts/get-window-title delete mode 100755 home-manager/modules/eww/config/scripts/get-workspaces delete mode 100755 home-manager/modules/eww/config/scripts/volume delete mode 100644 home-manager/modules/eww/default.nix delete mode 100644 home-manager/modules/fish/conf.d/10_theme.fish delete mode 100644 home-manager/modules/fish/conf.d/20_alias.fish delete mode 100644 home-manager/modules/fish/conf.d/30_kubectl.fish delete mode 100644 home-manager/modules/fish/default.nix delete mode 100644 home-manager/modules/nixvim/default.nix create mode 100644 justfile create mode 100644 machines/desktop/default.nix delete mode 100644 machines/legion/configuration.nix delete mode 100644 machines/legion/hardware-configuration.nix delete mode 100644 machines/legion/home-manager.nix delete mode 100644 machines/legion/nvidia.nix create mode 100644 machines/qemu/default.nix delete mode 100644 machines/virtualbox/configuration.nix delete mode 100644 machines/virtualbox/home-manager.nix delete mode 100644 machines/wsl/configuration.nix delete mode 100644 machines/wsl/home-manager.nix create mode 100644 modules/astal/default.nix create mode 100644 modules/astal/src/.gitignore create mode 100644 modules/astal/src/app.ts create mode 100644 modules/astal/src/biome.json create mode 100644 modules/astal/src/env.d.ts create mode 100644 modules/astal/src/icons/README.md create mode 100644 modules/astal/src/icons/chrome-custom-symbolic.svg create mode 100644 modules/astal/src/icons/circle-filled-symbolic.svg create mode 100644 modules/astal/src/icons/circle-symbolic.svg create mode 100644 modules/astal/src/icons/firefox-custom-symbolic.svg create mode 100644 modules/astal/src/icons/git-symbolic.svg create mode 100644 modules/astal/src/icons/microphone-custom-symbolic.svg create mode 100644 modules/astal/src/icons/plus-symbolic.svg create mode 100644 modules/astal/src/icons/python-symbolic.svg create mode 100644 modules/astal/src/icons/star-filled-symbolic.svg create mode 100644 modules/astal/src/icons/terminal-symbolic.svg create mode 100644 modules/astal/src/icons/vscode-custom-symbolic.svg create mode 100644 modules/astal/src/package-lock.json create mode 100644 modules/astal/src/package.json create mode 100644 modules/astal/src/src/bar/Bar.scss create mode 100644 modules/astal/src/src/bar/Bar.tsx create mode 100644 modules/astal/src/src/bar/SecondaryBar.tsx create mode 100644 modules/astal/src/src/bar/sections/Dropdown.scss create mode 100644 modules/astal/src/src/bar/sections/Dropdown.tsx create mode 100644 modules/astal/src/src/bar/sections/Media.scss create mode 100644 modules/astal/src/src/bar/sections/Media.tsx create mode 100644 modules/astal/src/src/bar/sections/Playback.scss create mode 100644 modules/astal/src/src/bar/sections/Playback.tsx create mode 100644 modules/astal/src/src/bar/sections/Workspace.scss create mode 100644 modules/astal/src/src/bar/sections/Workspace.tsx create mode 100644 modules/astal/src/src/config.ts create mode 100644 modules/astal/src/src/main.scss create mode 100644 modules/astal/src/src/main.ts create mode 100644 modules/astal/src/src/notification/Notification.scss create mode 100644 modules/astal/src/src/notification/Notification.tsx create mode 100644 modules/astal/src/src/notification/NotificationPopups.tsx create mode 100644 modules/astal/src/src/utils/gtk.ts create mode 100644 modules/astal/src/src/utils/io.ts create mode 100644 modules/astal/src/src/utils/monitors.ts create mode 100644 modules/astal/src/src/utils/timeout.ts create mode 100644 modules/astal/src/src/utils/var-map.ts create mode 100644 modules/astal/src/src/variables.scss create mode 100644 modules/astal/src/src/widgets.ts create mode 100644 modules/astal/src/tsconfig.json create mode 100644 modules/audio.nix create mode 100644 modules/base.nix create mode 100644 modules/btop.nix create mode 100644 modules/fonts.nix create mode 100644 modules/ghostty.nix create mode 100644 modules/greetd.nix create mode 100644 modules/hyprland/default.nix create mode 100644 modules/hyprland/hyprpolkitagent.nix create mode 100644 modules/monitors.nix create mode 100644 modules/user.nix delete mode 100644 nixos/configuration.nix create mode 120000 result create mode 100644 targets/graphical.nix create mode 100644 targets/shell.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af1da56 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +result/ \ No newline at end of file diff --git a/flake.lock b/flake.lock index 48ee087..f0a4fab 100644 --- a/flake.lock +++ b/flake.lock @@ -1,175 +1,64 @@ { "nodes": { - "devshell": { + "ags": { + "inputs": { + "astal": "astal", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1738087375, + "narHash": "sha256-GLyNtU9A2VN22jNRHZ2OXuFfTJLh8uEVVt+ftsKUX0c=", + "owner": "aylur", + "repo": "ags", + "rev": "a6a7a0adb17740f4c34a59902701870d46fbb6a4", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "ags", + "type": "github" + } + }, + "astal": { "inputs": { "nixpkgs": [ - "nixvim", + "ags", "nixpkgs" ] }, "locked": { - "lastModified": 1722113426, - "narHash": "sha256-Yo/3loq572A8Su6aY5GP56knpuKYRvM2a1meP9oJZCw=", - "owner": "numtide", - "repo": "devshell", - "rev": "67cce7359e4cd3c45296fb4aaf6a19e2a9c757ae", + "lastModified": 1737670815, + "narHash": "sha256-ZCxxshGN7XooabArcoGkYSNx5yVunqjKJi2aTv6cznI=", + "owner": "aylur", + "repo": "astal", + "rev": "127e9cdcbf173846a3c40ddc0abfbb038df48042", "type": "github" }, "original": { - "owner": "numtide", - "repo": "devshell", + "owner": "aylur", + "repo": "astal", "type": "github" } }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_2": { - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "revCount": 57, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, - "flake-compat_3": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": [ - "nixvim", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1722555600, - "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": "flake-compat_3", - "gitignore": "gitignore", - "nixpkgs": [ - "nixvim", - "nixpkgs" - ], - "nixpkgs-stable": [ - "nixvim", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1724440431, - "narHash": "sha256-9etXEOUtzeMgqg1u0wp+EdwG7RpmrAZ2yX516bMj2aE=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "c8a54057aae480c56e28ef3e14e4960628ac495b", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { + "astal_2": { "inputs": { "nixpkgs": [ - "nixvim", - "git-hooks", "nixpkgs" ] }, "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "lastModified": 1738063137, + "narHash": "sha256-lPuk+JaqFx7DFWAhf+PYcY4NF2Ebr5YRGLO/3299hHI=", + "owner": "aylur", + "repo": "astal", + "rev": "cb1578a7917339a9df9fa87773a739a0249501e2", "type": "github" }, "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", + "owner": "aylur", + "repo": "astal", "type": "github" } }, @@ -180,200 +69,68 @@ ] }, "locked": { - "lastModified": 1720042825, - "narHash": "sha256-A0vrUB6x82/jvf17qPCpxaM+ulJnD8YZwH9Ci0BsAzE=", + "lastModified": 1736373539, + "narHash": "sha256-dinzAqCjenWDxuy+MqUQq0I4zUSfaCvN9rzuCmgMZJY=", "owner": "nix-community", "repo": "home-manager", - "rev": "e1391fb22e18a36f57e6999c7a9f966dc80ac073", + "rev": "bd65bc3cde04c16755955630b344bc9e35272c56", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-24.05", + "ref": "release-24.11", "repo": "home-manager", "type": "github" } }, - "home-manager_2": { - "inputs": { - "nixpkgs": [ - "nixvim", - "nixpkgs" - ] - }, + "nixlib": { "locked": { - "lastModified": 1720042825, - "narHash": "sha256-A0vrUB6x82/jvf17qPCpxaM+ulJnD8YZwH9Ci0BsAzE=", + "lastModified": 1736643958, + "narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=", "owner": "nix-community", - "repo": "home-manager", - "rev": "e1391fb22e18a36f57e6999c7a9f966dc80ac073", + "repo": "nixpkgs.lib", + "rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-24.05", - "repo": "home-manager", + "repo": "nixpkgs.lib", "type": "github" } }, - "hyprcursor": { + "nixos-generators": { "inputs": { - "hyprlang": [ - "hyprland", - "hyprlang" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1712434681, - "narHash": "sha256-qwmR2p1oc48Bj7gUDvb1oGL19Rjs2PmEmk4ChV01A5o=", - "owner": "hyprwm", - "repo": "hyprcursor", - "rev": "818d8c4b69e0997483d60b75f701fe14b561a7a3", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprcursor", - "type": "github" - } - }, - "hyprland": { - "inputs": { - "hyprcursor": "hyprcursor", - "hyprland-protocols": "hyprland-protocols", - "hyprlang": "hyprlang", - "nixpkgs": "nixpkgs", - "systems": "systems", - "wlroots": "wlroots", - "xdph": "xdph" - }, - "locked": { - "lastModified": 1712766830, - "narHash": "sha256-WXD4j/hzoGQxakwFmvFrZD/yP4FxUrBc1kUhvmYh84k=", - "owner": "hyprwm", - "repo": "Hyprland", - "rev": "558d1be7e3c9242b39fe78efe74ada1298112892", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "Hyprland", - "type": "github" - } - }, - "hyprland-protocols": { - "inputs": { - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1691753796, - "narHash": "sha256-zOEwiWoXk3j3+EoF3ySUJmberFewWlagvewDRuWYAso=", - "owner": "hyprwm", - "repo": "hyprland-protocols", - "rev": "0c2ce70625cb30aef199cb388f99e19a61a6ce03", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprland-protocols", - "type": "github" - } - }, - "hyprlang": { - "inputs": { - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1711671891, - "narHash": "sha256-C/Wwsy/RLxHP1axFFl+AnwJRWfd8gxDKKoa8nt8Qk3c=", - "owner": "hyprwm", - "repo": "hyprlang", - "rev": "c1402612146ba06606ebf64963a02bc1efe11e74", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprlang", - "type": "github" - } - }, - "nix-darwin": { - "inputs": { - "nixpkgs": [ - "nixvim", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1724469941, - "narHash": "sha256-+U5152FwmDD9EUOiFi5CFxCK6/yFESyDei9jEIlmUtI=", - "owner": "lnl7", - "repo": "nix-darwin", - "rev": "ea319a737939094b48fda9063fa3201ef2479aac", - "type": "github" - }, - "original": { - "owner": "lnl7", - "repo": "nix-darwin", - "type": "github" - } - }, - "nixos-wsl": { - "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils", + "nixlib": "nixlib", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1710519878, - "narHash": "sha256-0dbc10OBFUVYyXC+C+N6vRUd8xyBSRxkcZ4Egipbx0M=", + "lastModified": 1737057290, + "narHash": "sha256-3Pe0yKlCc7EOeq1X/aJVDH0CtNL+tIBm49vpepwL1MQ=", "owner": "nix-community", - "repo": "nixos-wsl", - "rev": "aef95bdb6800a3a2af7aa7083d6df03067da6592", + "repo": "nixos-generators", + "rev": "d002ce9b6e7eb467cd1c6bb9aef9c35d191b5453", "type": "github" }, "original": { "owner": "nix-community", - "repo": "nixos-wsl", + "repo": "nixos-generators", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1712439257, - "narHash": "sha256-aSpiNepFOMk9932HOax0XwNxbA38GOUVOiXfUVPOrck=", - "owner": "NixOS", + "lastModified": 1738023785, + "narHash": "sha256-BPHmb3fUwdHkonHyHi1+x89eXB3kA1jffIpwPVJIVys=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "ff0dbd94265ac470dda06a657d5fe49de93b4599", + "rev": "2b4230bf03deb33103947e2528cac2ed516c5c89", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", + "owner": "nixos", + "ref": "nixos-24.11", "repo": "nixpkgs", "type": "github" } @@ -394,198 +151,14 @@ "type": "github" } }, - "nixpkgs_2": { - "locked": { - "lastModified": 1724316499, - "narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "797f7dc49e0bc7fab4b57c021cdf68f595e47841", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixvim": { - "inputs": { - "devshell": "devshell", - "flake-compat": "flake-compat_2", - "flake-parts": "flake-parts", - "git-hooks": "git-hooks", - "home-manager": "home-manager_2", - "nix-darwin": "nix-darwin", - "nixpkgs": [ - "nixpkgs" - ], - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1724502615, - "narHash": "sha256-g206hhNghyxMO9Sdv9fD22MRgWQppws10x+oDHKDVdU=", - "owner": "nix-community", - "repo": "nixvim", - "rev": "764b89aa14543da7266719757cfcf0cce8c1679f", - "type": "github" - }, - "original": { - "owner": "nix-community", - "ref": "nixos-24.05", - "repo": "nixvim", - "type": "github" - } - }, "root": { "inputs": { + "ags": "ags", + "astal": "astal_2", "home-manager": "home-manager", - "hyprland": "hyprland", - "nixos-wsl": "nixos-wsl", - "nixpkgs": "nixpkgs_2", - "nixpkgs-unstable": "nixpkgs-unstable", - "nixvim": "nixvim", - "vscode-server": "vscode-server" - } - }, - "systems": { - "locked": { - "lastModified": 1689347949, - "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", - "owner": "nix-systems", - "repo": "default-linux", - "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default-linux", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": [ - "nixvim", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1724338379, - "narHash": "sha256-kKJtaiU5Ou+e/0Qs7SICXF22DLx4V/WhG1P6+k4yeOE=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "070f834771efa715f3e74cd8ab93ecc96fabc951", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, - "vscode-server": { - "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709622318, - "narHash": "sha256-bTscF0366xtoIXgH7Zq+Mn0mpX3w4h/2xKpHiYMyLNc=", - "owner": "nix-community", - "repo": "nixos-vscode-server", - "rev": "d0ed9b8cf1f0a71f110df9119489ab047e0726bd", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixos-vscode-server", - "type": "github" - } - }, - "wlroots": { - "flake": false, - "locked": { - "host": "gitlab.freedesktop.org", - "lastModified": 1709983277, - "narHash": "sha256-wXWIJLd4F2JZeMaihWVDW/yYXCLEC8OpeNJZg9a9ly8=", - "owner": "wlroots", - "repo": "wlroots", - "rev": "50eae512d9cecbf0b3b1898bb1f0b40fa05fe19b", - "type": "gitlab" - }, - "original": { - "host": "gitlab.freedesktop.org", - "owner": "wlroots", - "repo": "wlroots", - "rev": "50eae512d9cecbf0b3b1898bb1f0b40fa05fe19b", - "type": "gitlab" - } - }, - "xdph": { - "inputs": { - "hyprland-protocols": [ - "hyprland", - "hyprland-protocols" - ], - "hyprlang": [ - "hyprland", - "hyprlang" - ], - "nixpkgs": [ - "hyprland", - "nixpkgs" - ], - "systems": [ - "hyprland", - "systems" - ] - }, - "locked": { - "lastModified": 1709299639, - "narHash": "sha256-jYqJM5khksLIbqSxCLUUcqEgI+O2LdlSlcMEBs39CAU=", - "owner": "hyprwm", - "repo": "xdg-desktop-portal-hyprland", - "rev": "2d2fb547178ec025da643db57d40a971507b82fe", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "xdg-desktop-portal-hyprland", - "type": "github" + "nixos-generators": "nixos-generators", + "nixpkgs": "nixpkgs", + "nixpkgs-unstable": "nixpkgs-unstable" } } }, diff --git a/flake.nix b/flake.nix index 2ad3c46..d24e6b9 100644 --- a/flake.nix +++ b/flake.nix @@ -2,84 +2,52 @@ description = "Your new nix config"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; + nixos-generators = { + url = "github:nix-community/nixos-generators"; + inputs.nixpkgs.follows = "nixpkgs"; + }; home-manager = { - url = "github:nix-community/home-manager/release-24.05"; + url = "github:nix-community/home-manager/release-24.11"; inputs.nixpkgs.follows = "nixpkgs"; }; - - nixos-wsl = { - url = "github:nix-community/nixos-wsl"; + astal = { + url = "github:aylur/astal"; inputs.nixpkgs.follows = "nixpkgs"; }; - - vscode-server = { - url = "github:nix-community/nixos-vscode-server"; + ags = { + url = "github:aylur/ags"; inputs.nixpkgs.follows = "nixpkgs"; }; - - nixvim = { - url = "github:nix-community/nixvim/nixos-24.05"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - - hyprland.url = "github:hyprwm/Hyprland"; }; outputs = { - self, nixpkgs, nixpkgs-unstable, + nixos-generators, home-manager, - nixos-wsl, - vscode-server, - nixvim, ... } @ inputs: let - inherit (self) outputs; + system = "x86_64-linux"; + stateVersion = "24.11"; + pkgs = nixpkgs.legacyPackages.${system}; + mkSystem = username: machine: + nixpkgs.lib.nixosSystem { + system = system; + specialArgs = {inherit inputs username machine stateVersion;}; + modules = [ + (import (./machines + "/${machine}")) + home-manager.nixosModules.home-manager + nixos-generators.nixosModules.all-formats + ]; + }; in { # Available through 'nixos-rebuild --flake .#wsl' + formatter.${system} = pkgs.alejandra; nixosConfigurations = { - virtualbox = nixpkgs.lib.nixosSystem { - specialArgs = { - inherit inputs outputs; - username = "nickolaj"; - hostname = "virtualbox"; - }; - modules = [ - nixos-wsl.nixosModules.wsl - vscode-server.nixosModules.default - ./nixos/configuration.nix - ./machines/virtualbox/configuration.nix - ]; - }; - wsl = nixpkgs.lib.nixosSystem { - specialArgs = { - inherit inputs outputs; - username = "nickolaj"; - hostname = "wsl"; - }; - modules = [ - nixos-wsl.nixosModules.wsl - vscode-server.nixosModules.default - ./nixos/configuration.nix - ./machines/wsl/configuration.nix - ]; - }; - legion = nixpkgs.lib.nixosSystem { - specialArgs = { - inherit inputs outputs; - username = "nickolaj"; - hostname = "legion"; - }; - modules = [ - nixos-wsl.nixosModules.wsl - vscode-server.nixosModules.default - ./nixos/configuration.nix - ./machines/legion/configuration.nix - ]; - }; + desktop = mkSystem "nickolaj" "desktop"; + qemu = mkSystem "nickolaj" "qemu"; }; }; } diff --git a/home-manager/default.nix b/home-manager/default.nix deleted file mode 100644 index 5401882..0000000 --- a/home-manager/default.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ - pkgs, - inputs, - username, - ... -}: { - imports = [ - ./modules/fish/default.nix - ./modules/nixvim/default.nix - ]; - - nixpkgs = { - config = { - allowUnfree = true; - allowUnfreePredicate = _: true; - }; - }; - - home = { - username = username; - homeDirectory = "/home/${username}"; - }; - - home.packages = with pkgs; [ - fzf - ripgrep - fd - git - jq - httpie - git - gh - ]; - - programs.git = { - enable = true; - userName = "Nickolaj Jepsen"; - userEmail = "nickolaj@fireproof.website"; - extraConfig = { - push = { - autoSetupRemote = true; - }; - pull = { - rebase = true; - }; - }; - }; - - home.stateVersion = "23.11"; -} diff --git a/home-manager/modules/eww/config/eww.scss b/home-manager/modules/eww/config/eww.scss deleted file mode 100644 index 284b520..0000000 --- a/home-manager/modules/eww/config/eww.scss +++ /dev/null @@ -1,112 +0,0 @@ -$anim: .2s cubic-bezier(0.4, 0.0, 0.2, 1); -$anim-slow: .4s cubic-bezier(0.4, 0.0, 0.2, 1); - -$background: #1C1C1C; -$background-alt: #2b2b2b; -$foreground: #C5C5C5; -$accent: #CF6A4C; - - -* { - all: unset; //Unsets everything so you can style everything from scratch - font-family: Hack Nerd Regular; - color: $foreground; - font-size: 16px -} - -.bar { - background-color: $background; - border-bottom: 2px solid $accent; - padding: 0 12px; -} - -.workspace-entry { - border: 2px solid $foreground; - border-radius: 50%; - min-width: 10px; - min-height: 10px; - margin: 5px 5px; - transition: background-color $anim, opacity $anim, border $anim; - opacity: .2; - - &.has-windows { - opacity: 1; - } - - &.active { - opacity: 1; - background-color: $foreground; - border-color: $foreground - } -} - -.datetime { - .time { - font-weight: bold; - margin-right: 15px; - } -} - -.side-bar { - padding: 0 5px; - opacity: .4; - background-color: transparent; - transition: background-color $anim-slow, opacity $anim-slow, border $anim-slow; - border-bottom: 2px solid transparent; - - &.active { - opacity: 1; - background-color: $background; - border-bottom: 2px solid $accent; - } - - .workspace-entry { - margin: 5px 2px; - } - - &.left-bar { - &.active { - border-left: 2px solid $accent; - } - border-left: 2px solid transparent; - border-bottom-left-radius: 12px; - } - - &.right-bar { - &.active { - border-right: 2px solid $accent; - } - border-right: 2px solid transparent; - border-bottom-right-radius: 12px; - } -} - -.scale { - trough { - background-color: $background-alt; - border-radius: 10px; - min-height: 5px; - min-width: 80px; - - highlight { - background-color: $accent; - border-radius: 10px; - } - } -} - -.music { - .music-control { - font-size: 10px; - } - .music-timer { - font-size: 12px; - } - .scale { - margin: 0 10px; - } - padding: 0 15px; - margin: 0 15px; - border-right: 2px solid #CF6A4C; - border-left: 2px solid #CF6A4C; -} diff --git a/home-manager/modules/eww/config/eww.yuck b/home-manager/modules/eww/config/eww.yuck deleted file mode 100644 index 0fe841d..0000000 --- a/home-manager/modules/eww/config/eww.yuck +++ /dev/null @@ -1,98 +0,0 @@ -; WINDOWS - -(defwindow primary - :monitor 0 - :stacking "fg" - :exclusive true - :geometry (geometry :x "0%" - :y "0%" - :width "100%" - :height "20px" - :anchor "top center") - (primary_bar)) - -(defwindow left - :monitor 1 - :stacking "fg" - :exclusive false - :geometry (geometry :x "0%" - :y "0%" - :width "100%" - :height "20px" - :anchor "top center") - (left_bar)) - -(defwindow right - :monitor 2 - :stacking "fg" - :exclusive false - :geometry (geometry :x "0%" - :y "0%" - :width "100%" - :height "20px" - :anchor "top center") - (right_bar)) - -; VARIABLES - -(defpoll battery :interval "1s" "scripts/battery-level") -(deflisten workspaces :initial "{}" "bash ~/.config/eww/scripts/get-workspaces") -(deflisten current_workspace :initial "1" "bash ~/.config/eww/scripts/get-active-workspace") -(deflisten current_monitor :initial "DP-1" "bash ~/.config/eww/scripts/get-active-monitor") -(deflisten window :initial "..." "sh ~/.config/eww/scripts/get-window-title") -(deflisten volume :initial "{}" "sh ~/.config/eww/scripts/volume") -(defpoll time :interval "10s" "date '+%H:%M'") -(defpoll date :interval "10s" "date '+%Y-%m-%d'") - -; BARS - -(defwidget primary_bar [] - (box :class "bar primary-bar ${current_monitor == 'DP-1' ? 'active' : ''}" - (box :class "primary-left" :space-evenly false :halign "start" - (datetime) - (workspaces :ids "[\"1\",\"2\",\"3\",\"4\",\"5\"]")) - (label :halign "center" :text "${window}") - (controls :halign "end"))) - -(defwidget left_bar [] - (box :class "bar side-bar left-bar ${current_monitor == 'HDMI-A-1' ? 'active' : ''}" :halign "end" - (workspaces :ids "[\"6\",\"7\"]"))) - -(defwidget right_bar [] - (box :class "bar side-bar right-bar ${current_monitor == 'DP-3' ? 'active' : ''}" :halign "start" - (workspaces :ids "[\"8\",\"9\"]"))) - -; WIDGETS - -(defwidget workspaces [ids] - (box :space-evenly false :spacing 10 :class "workspaces" - (for id in ids - (eventbox :onclick "hyprctl dispatch workspace ${id}" - (box :class "workspace-entry ${current_workspace == id ? 'active' : 'inactive'} ${(workspaces?.[id]?.windows?:0) >= 1 ? 'has-windows' : 'no-windows'}"))))) - -(defwidget datetime [] - (box :space-evenly false :class "datetime" :spacing 10 - (label :class "date" :text "${date}") - (label :class "time" :text "${time}"))) - -(defwidget controls [] - (box :space-evenly false :class "controls" :halign "end" - (metric :icon {volume?.["icon"]?:""} - :value {volume?.["audio"] != "1" ? 0 : volume?.["percent"]} - :onchange "~/.config/eww/scripts/volume setvol SINK {}" - :onclick "~/.config/eww/scripts/volume mute SINK"))) - -(defwidget metric [value ?icon ?onchange ?onclick] - (box :space-evenly false :class "metric" :spacing 10 :halign "end" :visible {(value?:-1) != -1} - (scale :class "scale" - :min 0 - :max 101 - :flipped true - :orientation 'h' - :active {(onchange?:"") != ""} - :onchange {onchange} - :value {value?:0}) - (eventbox :active {(onclick?:"") != ""} :onclick {onclick} - (box - (label :width 1 :visible {icon != ""} :class "icon" :text {icon}) - (label :width 35 :class "label" :text "${value}%"))))) diff --git a/home-manager/modules/eww/config/scripts/battery-level b/home-manager/modules/eww/config/scripts/battery-level deleted file mode 100644 index fb9a53d..0000000 --- a/home-manager/modules/eww/config/scripts/battery-level +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -cat /sys/class/power_supply/BAT0/capacity \ No newline at end of file diff --git a/home-manager/modules/eww/config/scripts/battery-status b/home-manager/modules/eww/config/scripts/battery-status deleted file mode 100644 index 3d51bbd..0000000 --- a/home-manager/modules/eww/config/scripts/battery-status +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -cat /sys/class/power_supply/BAT0/status \ No newline at end of file diff --git a/home-manager/modules/eww/config/scripts/change-active-workspace b/home-manager/modules/eww/config/scripts/change-active-workspace deleted file mode 100755 index ad6470f..0000000 --- a/home-manager/modules/eww/config/scripts/change-active-workspace +++ /dev/null @@ -1,21 +0,0 @@ -#! /bin/bash -function clamp { - min=$1 - max=$2 - val=$3 - python -c "print(max($min, min($val, $max)))" -} - -direction=$1 -current=$2 -if test "$direction" = "down" -then - target=$(clamp 1 10 $(($current+1))) - echo "jumping to $target" - hyprctl dispatch workspace $target -elif test "$direction" = "up" -then - target=$(clamp 1 10 $(($current-1))) - echo "jumping to $target" - hyprctl dispatch workspace $target -fi \ No newline at end of file diff --git a/home-manager/modules/eww/config/scripts/get-active-monitor b/home-manager/modules/eww/config/scripts/get-active-monitor deleted file mode 100755 index 1fc8034..0000000 --- a/home-manager/modules/eww/config/scripts/get-active-monitor +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -hyprctl monitors -j | jq '.[] | select(.focused) | .name' - -socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | - stdbuf -o0 awk -F '>>|,' -e '/^focusedmon>>/ {print $2}' \ No newline at end of file diff --git a/home-manager/modules/eww/config/scripts/get-active-workspace b/home-manager/modules/eww/config/scripts/get-active-workspace deleted file mode 100755 index 0ebac7b..0000000 --- a/home-manager/modules/eww/config/scripts/get-active-workspace +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -hyprctl monitors -j | jq '.[] | select(.focused) | .activeWorkspace.id' - -socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | - stdbuf -o0 awk -F '>>|,' -e '/^workspace>>/ {print $2}' -e '/^focusedmon>>/ {print $3}' \ No newline at end of file diff --git a/home-manager/modules/eww/config/scripts/get-window-title b/home-manager/modules/eww/config/scripts/get-window-title deleted file mode 100755 index cb08116..0000000 --- a/home-manager/modules/eww/config/scripts/get-window-title +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -hyprctl activewindow -j | jq --raw-output .title | sed 's/\(.\{120\}\).*/\1.../' -socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | stdbuf -o0 awk -F '>>|,' '/^activewindow>>/{print $3}' | sed 's/\(.\{120\}\).*/\1.../' diff --git a/home-manager/modules/eww/config/scripts/get-workspaces b/home-manager/modules/eww/config/scripts/get-workspaces deleted file mode 100755 index fcc34e2..0000000 --- a/home-manager/modules/eww/config/scripts/get-workspaces +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -spaces (){ - hyprctl workspaces -j | jq 'map({key: .id | tostring, value: .}) | from_entries' -Mc -} - -spaces -socat -u UNIX-CONNECT:/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | while read -r line; do - spaces -done \ No newline at end of file diff --git a/home-manager/modules/eww/config/scripts/volume b/home-manager/modules/eww/config/scripts/volume deleted file mode 100755 index cf77a02..0000000 --- a/home-manager/modules/eww/config/scripts/volume +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash - -# FROM HERE: https://github.com/end-4/dots-hyprland/blob/6c09531bc2d168e79b2a76f0174b297a3b66b9b0/.config/eww/scripts/volume - -cd ~/.config/eww - -volicons=("" "󰖀" "󰕾") -XDG_CACHE_HOME="$HOME/.cache" -lock=0 - -vol() { - wpctl get-volume @DEFAULT_AUDIO_$1@ | awk '{print int($2*100)}' -} -ismuted() { - wpctl get-volume @DEFAULT_AUDIO_"$1"@ | rg -i muted - echo $? -} -setvol() { - wpctl set-volume @DEFAULT_AUDIO_"$1"@ "$(awk -v n="$2" 'BEGIN{print (n / 100)}')" -} -setmute() { - wpctl set-mute @DEFAULT_AUDIO_"$1"@ toggle -} - -if [ "$1" = "--once" ]; then - lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}') - ismuted=$(ismuted "SINK") - - if [ "$ismuted" = 1 ]; then - icon="${volicons[$lvl]}" - else - icon="󰝟" - fi - audio=1 - if [ "$(wpctl status | grep 'MUTED')" == "" ]; then - audio=1 - else - audio=0 - fi - - echo '{"icon":"'"$icon"'","audio":"'"$audio"'","percent":"'"$(vol "SINK")"'","microphone":"'"$(vol "SOURCE")"'"}' - exit 0 -fi - -if [ "$1" = "mute" ]; then - if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then - echo "Can only mute SINK or SOURCE"; exit 1 - fi - setmute "$2" -elif [ "$1" = "setvol" ]; then - if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then - echo "Can only set volume for SINK or SOURCE"; exit 1 - elif [ "$3" -lt 1 ] || [ "$3" -gt 100 ]; then - echo "Volume must be between 1 and 100"; exit 1 - fi - setvol "$2" "$3" -else - # initial values - lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}') - ismuted=$(ismuted "SINK") - - if [ "$ismuted" = 1 ]; then - icon="${volicons[$lvl]}" - else - icon="󰝟" - fi - audio=1 - if [ "$(wpctl status | grep 'MUTED')" == "" ]; then - audio=1 - else - audio=0 - fi - - echo '{"icon":"'"$icon"'","audio":"'"$audio"'","percent":"'"$(vol "SINK")"'","microphone":"'"$(vol "SOURCE")"'"}' - - # event loop - pactl subscribe | rg --line-buffered "on sink" | while read -r _; do - lvl=$(awk -v n="$(vol "SINK")" 'BEGIN{print int(n/34)}') - ismuted=$(ismuted "SINK") - - if [ "$ismuted" = 1 ]; then - icon="${volicons[$lvl]}" - else - icon="󰝟" - fi - audio=1 - if [ "$(wpctl status | grep 'MUTED')" == "" ]; then - audio=1 - else - audio=0 - fi - - echo '{"icon":"'"$icon"'","audio":"'"$audio"'","percent":"'"$(vol "SINK")"'","microphone":"'"$(vol "SOURCE")"'"}' - done -fi \ No newline at end of file diff --git a/home-manager/modules/eww/default.nix b/home-manager/modules/eww/default.nix deleted file mode 100644 index 4b9a381..0000000 --- a/home-manager/modules/eww/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ - pkgs, - config, - ... -}: { - home.file."${config.xdg.configHome}/eww" = { - source = ./config; - recursive = true; - }; - - home.packages = with pkgs; [ - socat - eww - ]; -} \ No newline at end of file diff --git a/home-manager/modules/fish/conf.d/10_theme.fish b/home-manager/modules/fish/conf.d/10_theme.fish deleted file mode 100644 index 0596940..0000000 --- a/home-manager/modules/fish/conf.d/10_theme.fish +++ /dev/null @@ -1,38 +0,0 @@ -set -gx theme_date_timezone Europe/Copenhagen -set -gx theme_date_format "+%a %H:%M" - -function bobthefish_colors -S -d 'Define a custom bobthefish color scheme' - - # Optionally include a base color scheme - # __bobthefish_colors default - - # Then override everything you want! - # Note that these must be defined with `set -x` - set -x color_initial_segment_exit white red --bold - set -x color_initial_segment_su white green --bold - set -x color_initial_segment_jobs white blue --bold - - set -x color_path 161616 888 - set -x color_path_basename 161616 white - set -x color_path_nowrite magenta black - set -x color_path_nowrite_basename magenta black --bold - - set -x color_repo green black - set -x color_repo_work_tree black black --bold - set -x color_repo_dirty brred black - set -x color_repo_staged yellow black - - set -x color_vi_mode_default brblue black --bold - set -x color_vi_mode_insert brgreen black --bold - set -x color_vi_mode_visual bryellow black --bold - - set -x color_vagrant brcyan black - set -x color_k8s magenta white --bold - set -x color_username black white --bold - set -x color_hostname black white - set -x color_rvm brmagenta black --bold - set -x color_virtualfish brblue black --bold - set -x color_virtualgo brblue black --bold - set -x color_desk brblue black --bold - set -g theme_display_nix yes -end \ No newline at end of file diff --git a/home-manager/modules/fish/conf.d/20_alias.fish b/home-manager/modules/fish/conf.d/20_alias.fish deleted file mode 100644 index c7fc16f..0000000 --- a/home-manager/modules/fish/conf.d/20_alias.fish +++ /dev/null @@ -1,37 +0,0 @@ -if type -q nvim - alias vim nvim -end - -if type -q docker - abbr d "docker" -end - -if type -q docker-compose - abbr dc "docker-compose" -end - -if type -q snap - set PYCHARM_SNAP (snap list | grep 'pycharm' | awk '{ print $1 }') - set HELM_SNAP (snap list | grep 'helm' | awk '{ print $1 }') - if test -n "$HELM_SNAP" - alias helm "snap run $HELM_SNAP" - end - if test -n "$PYCHARM_SNAP" - alias pycharm "snap run $PYCHARM_SNAP" - end -end - -if type -q to - abbr z "to" -end - -if type -q broot - abbr br "broot" -end - -if type -q terraform - abbr tf "terraform" - abbr tfp "terraform plan" - abbr tfa "terraform apply" - abbr tfi "terraform init" -end \ No newline at end of file diff --git a/home-manager/modules/fish/conf.d/30_kubectl.fish b/home-manager/modules/fish/conf.d/30_kubectl.fish deleted file mode 100644 index 1a8bd78..0000000 --- a/home-manager/modules/fish/conf.d/30_kubectl.fish +++ /dev/null @@ -1,47 +0,0 @@ -set __kube_verbs get describe delete edit -set __kube_verbs_short g d rm e -set __kube_resource pods deployments services ingresses configmaps daemonsets statefulsets namespace namespace -set __kube_resource_short p d s i c ds ss n ns - -function __echo_kubeexec; - set _flag_namespace (kubectl config view --minify --output 'jsonpath={..namespace}') - if test -z "$_flag_namespace" - set _flag_namespace default - end - - set _flag_pod shop - set POD (kubectl get pods --namespace $_flag_namespace 2>/dev/null | grep "^$_flag_pod" | grep Running | head -n1 | awk '{ print $1 }') - if test -z "$POD" - echo "kubectl exec --namespace $_flag_namespace -it" - return - end - echo "kubectl exec --namespace $_flag_namespace -it $POD --" -end - -if type -q kubectl - for verb_index in (seq (count $__kube_verbs)) - abbr "k$__kube_verbs_short[$verb_index]" "kubectl $__kube_verbs[$verb_index]" - for res_index in (seq (count $__kube_resource)) - abbr "k$__kube_verbs_short[$verb_index]$__kube_resource_short[$res_index]" "kubectl $__kube_verbs[$verb_index] $__kube_resource[$res_index]" - end - end - - abbr k kubectl - abbr kl kubectl logs -f - abbr kgl kubectl logs -f - abbr kaf kubectl apply -f - abbr kr kubectl rollout - abbr krs kubectl rollout status - abbr krr kubectl rollout restart - abbr kt kubectl top - abbr ktp kubectl top pods - abbr ktn kubectl top nodes - abbr kpf kubectl port-forward - abbr kfp kubectl port-forward - - alias kns "kubectl config view --minify --output 'jsonpath={..namespace}'" - abbr ksns "kubectl config set-context --current --namespace" - - abbr kexec --function __echo_kubeexec - abbr kmanage "kexec python3 /src/lib/manage.py" -end \ No newline at end of file diff --git a/home-manager/modules/fish/default.nix b/home-manager/modules/fish/default.nix deleted file mode 100644 index 5119b9a..0000000 --- a/home-manager/modules/fish/default.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ - pkgs, - config, - ... -}: { - home.file."${config.home.homeDirectory}/.config/fish/conf.d/" = { source = ./conf.d; recursive = true;}; - - programs.fish = { - enable = true; - plugins = with pkgs.fishPlugins; [ - { - name = "fzf"; - src = fzf.src; - } - { - name = "bobthefish"; - src = bobthefish.src; - } - { - name = "to"; - src = pkgs.fetchFromGitHub { - owner = "joehillen"; - repo = "to-fish"; - rev = "52b151cfe67c00cb64d80ccc6dae398f20364938"; - hash = "sha256-DfDsU/qY2XdYlkLISIOv02ggHfKEpb+YompNWWjs5/A="; - }; - } - ]; - }; - - # Init fish from bash - programs.bash = { - enable = true; - initExtra = '' - if [[ $(${pkgs.procps}/bin/ps --no-header --pid=$PPID --format=comm) != "fish" && -z ''${BASH_EXECUTION_STRING} ]] - then - shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION="" - exec ${pkgs.fish}/bin/fish $LOGIN_OPTION - fi - ''; - }; -} \ No newline at end of file diff --git a/home-manager/modules/nixvim/default.nix b/home-manager/modules/nixvim/default.nix deleted file mode 100644 index 9ac9288..0000000 --- a/home-manager/modules/nixvim/default.nix +++ /dev/null @@ -1,90 +0,0 @@ -{ inputs, ... }: -{ - imports = [ - inputs.nixvim.homeManagerModules.nixvim - ]; - - programs.neovim = { - defaultEditor = true; - }; - - programs.nixvim = { - enable = true; - - opts = { - number = true; - relativenumber = false; - tabstop = 4; - shiftwidth = 4; - expandtab = false; - }; - - globals.mapleader = " "; - - keymaps = [ - { - key = "jk"; - action = ""; - mode = "i"; - } - { - key = "o"; - action = ":NvimTreeToggle"; - options = { - silent = true; - }; - } - ]; - - colorschemes.base16 = { - enable = true; - colorscheme = "twilight"; - }; - - plugins = { - barbecue.enable = true; - nvim-colorizer.enable = true; - gitsigns.enable = true; - indent-blankline.enable = true; - surround.enable = true; - bufferline.enable = true; - nvim-autopairs.enable = true; - lsp-format.enable = true; - lightline.enable = true; - intellitab.enable = true; - which-key.enable = true; - - telescope = { - enable = true; - keymaps = { - "ff" = "find_files"; - }; - }; - - nvim-tree = { - enable = true; - filters.custom = [ - ".git" - ]; - }; - - treesitter = { - enable = true; - nixGrammars = true; - }; - - lsp = { - enable = true; - servers.nil-ls.enable = true; - }; - - lspkind = { - enable = true; - }; - - cmp = { - enable = true; - }; - }; - }; -} \ No newline at end of file diff --git a/justfile b/justfile new file mode 100644 index 0000000..4dc36e6 --- /dev/null +++ b/justfile @@ -0,0 +1,30 @@ +build-vm: + @git add . + echo "Building VM..." + nix build .#nixosConfigurations.qemu.config.formats.qcow + echo "VM built." + @sudo chmod 777 result/nixos.qcow2 + echo "VM permissions set." + +reload-vm: build-vm + @sleep 5 + echo "Reloading VM..." + -virsh destroy nixos + virsh start nixos + echo "VM reloaded." + +setup-vm: + virsh pool-define-as nixos dir - - - - $HOME/.local/libvirt/images/nixos + virsh pool-build nixos + virsh pool-start nixos + virt-install \ + --name nixos \ + --os-variant=nixos-24.05 \ + --memory 8192 \ + --vcpus=4,maxvcpus=8 \ + --cpu host \ + --disk result/nixos.qcow2 \ + --network user \ + --virt-type kvm \ + --import \ + --graphics spice diff --git a/machines/desktop/default.nix b/machines/desktop/default.nix new file mode 100644 index 0000000..8f0a076 --- /dev/null +++ b/machines/desktop/default.nix @@ -0,0 +1,15 @@ +{ + username, + stateVersion, + ... +}: { + imports = [ + ../../targets/graphical.nix + ../../targets/shell.nix + ]; + + config = { + user.username = username; + system.stateVersion = stateVersion; + }; +} diff --git a/machines/legion/configuration.nix b/machines/legion/configuration.nix deleted file mode 100644 index 8b22832..0000000 --- a/machines/legion/configuration.nix +++ /dev/null @@ -1,52 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - -{ config, pkgs, username, inputs, ... }: - -{ - imports = - [ # Include the results of the hardware scan. - ./hardware-configuration.nix - ./nvidia.nix - ]; - - # Bootloader. - boot.loader.grub.enable = true; - boot.loader.grub.device = "/dev/nvme0n1"; - boot.loader.grub.useOSProber = true; - - environment.sessionVariables = { - WLR_NO_HARDWARE_CURSORS = "1"; - NIXOS_OZONE_WL = "1"; - }; - - programs.hyprland.enable = true; - programs.hyprland.xwayland.enable = true; - - services.automatic-timezoned.enable = true; - - virtualisation.docker.enable = true; - - fonts.packages = with pkgs; [ - (nerdfonts.override { fonts = [ "Hack" ]; }) - ]; - - home-manager.users.${username}.imports = [ - ./home-manager.nix - ]; - - environment.systemPackages = [ - pkgs.gtk3 - ]; - - networking.wireless.enable = true; - networking.wireless.networks = { - Brother = { - psk = "fireproof"; - }; - "Drakenvej12-5G-1" = { - psk = "Eg9928nt."; - }; - }; -} diff --git a/machines/legion/hardware-configuration.nix b/machines/legion/hardware-configuration.nix deleted file mode 100644 index 665e071..0000000 --- a/machines/legion/hardware-configuration.nix +++ /dev/null @@ -1,35 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: - -{ - imports = - [ (modulesPath + "/installer/scan/not-detected.nix") - ]; - - boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ "kvm-intel" ]; - boot.extraModulePackages = [ ]; - - fileSystems."/" = - { device = "/dev/disk/by-uuid/ba0e4c19-b23f-4099-9107-167e4babe3f2"; - fsType = "ext4"; - }; - - swapDevices = - [ { device = "/dev/disk/by-uuid/61ff6a39-c765-43c4-ae64-09f94478e0aa"; } - ]; - - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp7s0.useDHCP = lib.mkDefault true; - # networking.interfaces.wlp0s20f3.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; -} diff --git a/machines/legion/home-manager.nix b/machines/legion/home-manager.nix deleted file mode 100644 index ec60800..0000000 --- a/machines/legion/home-manager.nix +++ /dev/null @@ -1,152 +0,0 @@ -{ - pkgs, - config, - ... -}: { - - nixpkgs.config.permittedInsecurePackages = [ - "electron-25.9.0" # Obsidian - ]; - - home.packages = with pkgs; [ - kitty - wofi - xfce.thunar - xterm - firefox - obsidian - - # Dev - nodejs_20 - supabase-cli - vscode - jetbrains.pycharm-professional - jetbrains.rust-rover - sublime-merge - ]; - programs.kitty = { - enable = true; - }; - - programs.vscode = { - enable = true; - enableUpdateCheck = true; - enableExtensionUpdateCheck = true; - extensions = with pkgs.vscode-extensions; [ - github.copilot - ms-python.python - ms-vscode-remote.remote-ssh - # ms-vscode-remote.remote-ssh-edit - ]; - userSettings = { - "window.titleBarStyle" = "custom"; - "remote.SSH.useLocalServer" = false; - "github.copilot.enable" = { - "*" = true; - }; - }; - }; - - wayland.windowManager.hyprland = { - enable = true; - package = pkgs.hyprland; - systemd.enable = true; - settings = { - "monitor" = ",preferred,auto,1"; - "exec-once" = "eww daemon & eww open primary"; - - workspace = [ - "1, monitor:eDP-1, default:true" - "2, monitor:eDP-1" - "3, monitor:eDP-1" - "4, monitor:eDP-1" - "5, monitor:eDP-1" - ]; - - layerrule = "noanim, wofi"; - - input = { - kb_layout = "dk"; - kb_options = "caps:backspace"; - follow_mouse = "2"; # Cursor focus will be detached from keyboard focus. Clicking on a window will move keyboard focus to that window. - - touchpad = { - natural_scroll = true; - }; - - sensitivity = "0"; # -1.0 - 1.0, 0 means no modification. - accel_profile = "flat"; - }; - - general = { - gaps_in = 5; - gaps_out = 10; - border_size = 2; - "col.inactive_border" = "rgb(2f2f2f)"; - "col.active_border" = "rgb(cf6a4c)"; - layout = "dwindle"; - }; - - cursor = { - no_warps = true; - }; - - dwindle = { - pseudotile = true; - preserve_split = true; - force_split = 2; - use_active_for_splits = true; - }; - - misc = { - focus_on_activate = true; - }; - - decoration = { - rounding = 5; - drop_shadow = true; - shadow_range = 4; - shadow_render_power = 3; - #col.shadow = "rgba(1a1a1aee)"; - }; - - "$mod" = "SUPER"; - bind = [ - "$mod, RETURN, exec, kitty" - "$mod, BACKSPACE, killactive" - "$mod, SPACE, exec, wofi --show drun" - - "$mod, q, workspace, 1" - "$mod, w, workspace, 2" - "$mod, e, workspace, 3" - "$mod, r, workspace, 4" - "$mod, t, workspace, 5" - "SUPER_SHIFT, q, movetoworkspace, 1" - "SUPER_SHIFT, w, movetoworkspace, 2" - "SUPER_SHIFT, e, movetoworkspace, 3" - "SUPER_SHIFT, r, movetoworkspace, 4" - "SUPER_SHIFT, t, movetoworkspace, 5" - - "$mod, left, movefocus, l" - "$mod, right, movefocus, r" - "$mod, up, movefocus, u" - "$mod, down, movefocus, d" - "$mod, h, movefocus, l" - "$mod, l, movefocus, r" - "$mod, k, movefocus, u" - "$mod, j, movefocus, d" - - "$mod, S, togglefloating," - "$mod, A, pseudo," # dwindle - "$mod, D, fullscreen," # dwindle - "$mod, BACKSLASH, togglesplit," # dwindle - "$mod, M, togglegroup," # dwindle - - ]; - bindm = [ - "$mod, mouse:272, movewindow" - "$mod, mouse:273, resizewindow" - ]; - }; - }; -} diff --git a/machines/legion/nvidia.nix b/machines/legion/nvidia.nix deleted file mode 100644 index 3962519..0000000 --- a/machines/legion/nvidia.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ config, lib, pkgs, ... }: -{ - - # Enable OpenGL - hardware.opengl = { - enable = true; - }; - - # Load nvidia driver for Xorg and Wayland - services.xserver.videoDrivers = ["nvidia"]; - - boot = { - blacklistedKernelModules = lib.mkDefault [ "nouveau" ]; - kernelParams = [ "nvidia-drm.fbdev=1" ]; - kernelModules = [ "kvm-intel" "nvidia" "i915" "nvidia_modeset" "nvidia_uvm" "nvidia_drm" ]; - }; - - hardware.nvidia = { - package = config.boot.kernelPackages.nvidiaPackages.stable; - nvidiaSettings = true; - modesetting.enable = true; - }; -} \ No newline at end of file diff --git a/machines/qemu/default.nix b/machines/qemu/default.nix new file mode 100644 index 0000000..115b40d --- /dev/null +++ b/machines/qemu/default.nix @@ -0,0 +1,18 @@ +{ + username, + stateVersion, + ... +}: { + imports = [ + ../../targets/graphical.nix + ../../targets/shell.nix + ]; + + config = { + user.username = username; + system.stateVersion = stateVersion; + monitor.primary.resolution = "1920x1080"; + services.qemuGuest.enable = true; + services.spice-vdagentd.enable = true; + }; +} diff --git a/machines/virtualbox/configuration.nix b/machines/virtualbox/configuration.nix deleted file mode 100644 index eabfc6a..0000000 --- a/machines/virtualbox/configuration.nix +++ /dev/null @@ -1,58 +0,0 @@ -# Do not modify this file! It was generated by ‘nixos-generate-config’ -# and may be overwritten by future invocations. Please make changes -# to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, username, inputs, ... }: - -{ - imports = [ ]; - environment.sessionVariables = { - WLR_NO_HARDWARE_CURSORS = "1"; - }; - #services.xserver.enable = true; - #services.xserver.displayManager.gdm = { - # enable = true; - #}; - programs.hyprland.enable = true; - programs.hyprland.package = inputs.hyprland.packages.${pkgs.system}.hyprland; - programs.hyprland.xwayland.enable = true; -# programs.hyprland.enableNvidiaPatches = true; - home-manager.users.${username}.imports = [ - ./home-manager.nix - ]; - - environment.systemPackages = [ - pkgs.gtk3 - ]; - - hardware.opengl = { - enable = true; - driSupport = true; - #extraPackages = with pkgs; [ - # vaapiVdpau - # libvdpau-va-gl - #]; - }; - - boot.initrd.availableKernelModules = [ "ohci_pci" "ehci_pci" "ahci" "sd_mod" ]; - boot.initrd.kernelModules = [ ]; - boot.kernelModules = [ ]; - boot.extraModulePackages = [ ]; - boot.loader.grub.device = "nodev"; - - fileSystems."/" = - { device = "/dev/disk/by-uuid/f222513b-ded1-49fa-b591-20ce86a2fe7f"; - fsType = "ext4"; - }; - - swapDevices = [ ]; - - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. - networking.useDHCP = lib.mkDefault true; - # networking.interfaces.enp0s3.useDHCP = lib.mkDefault true; - - nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; - virtualisation.virtualbox.guest.enable = true; -} diff --git a/machines/virtualbox/home-manager.nix b/machines/virtualbox/home-manager.nix deleted file mode 100644 index 62e49e8..0000000 --- a/machines/virtualbox/home-manager.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ - pkgs, - ... -}: { - home.packages = with pkgs; [ - nodejs_20 - kitty - alacritty - wofi - vscode - xfce.thunar - xterm - foot - firefox - ]; - programs.kitty = { - enable = true; - }; - wayland.windowManager.hyprland = { - enable = true; - package = pkgs.hyprland; - systemd.enable = true; - settings = { - "$mod" = "SUPER"; - bind = [ - "$mod, RETURN, exec, kitty" - "$mod, SPACE, exec, wofi --show drun" - ]; - }; - }; -} diff --git a/machines/wsl/configuration.nix b/machines/wsl/configuration.nix deleted file mode 100644 index a6f639c..0000000 --- a/machines/wsl/configuration.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - username, - pkgs, - ... -}: { - programs.nix-ld = { - enable = true; - }; - - wsl = { - enable = true; - defaultUser = username; - }; - - home-manager.users.${username}.imports = [ - ./home-manager.nix - ]; - - # Hacks to enable vscode - services.vscode-server.enable = true; - wsl.extraBin = with pkgs; [ - { src = "${coreutils}/bin/uname"; } - { src = "${coreutils}/bin/dirname"; } - { src = "${coreutils}/bin/readlink"; } - ]; -} diff --git a/machines/wsl/home-manager.nix b/machines/wsl/home-manager.nix deleted file mode 100644 index e69de29..0000000 diff --git a/modules/astal/default.nix b/modules/astal/default.nix new file mode 100644 index 0000000..86abcf0 --- /dev/null +++ b/modules/astal/default.nix @@ -0,0 +1,46 @@ +{ + inputs, + pkgs, + ... +}: let + packageName = "astal"; + + package = inputs.ags.lib.bundle { + inherit pkgs; + src = ./src; + name = packageName; + gtk4 = true; + entry = "app.ts"; + extraPackages = with inputs.ags.packages.${pkgs.system}; [ + battery + bluetooth + hyprland + network + tray + notifd + mpris + wireplumber + ]; + }; +in { + user.home-manager = { + systemd.user.services.astal = { + Unit = { + Description = "Astal"; + Documentation = "https://github.com/Aylur/astal"; + After = ["graphical-session.target"]; + }; + + Service = { + ExecStart = "${package}/bin/${packageName}"; + Restart = "on-failure"; + KillMode = "mixed"; + Slice = "app-graphical.slice"; + }; + + Install = { + WantedBy = ["graphical-session.target"]; + }; + }; + }; +} diff --git a/modules/astal/src/.gitignore b/modules/astal/src/.gitignore new file mode 100644 index 0000000..543df19 --- /dev/null +++ b/modules/astal/src/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +@girs/ +.idea/ diff --git a/modules/astal/src/app.ts b/modules/astal/src/app.ts new file mode 100644 index 0000000..540a094 --- /dev/null +++ b/modules/astal/src/app.ts @@ -0,0 +1,11 @@ +import { App } from "astal/gtk4"; +import main from "./src/main"; +import css from "./src/main.scss"; + +App.start({ + css, + icons: "./icons", + main: () => { + main(); + }, +}); diff --git a/modules/astal/src/biome.json b/modules/astal/src/biome.json new file mode 100644 index 0000000..4ea3dc2 --- /dev/null +++ b/modules/astal/src/biome.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": ["@girs/**"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "all": false + }, + "correctness": { + "useJsxKeyInIterable": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/modules/astal/src/env.d.ts b/modules/astal/src/env.d.ts new file mode 100644 index 0000000..ab20438 --- /dev/null +++ b/modules/astal/src/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/modules/astal/src/icons/README.md b/modules/astal/src/icons/README.md new file mode 100644 index 0000000..fed6c18 --- /dev/null +++ b/modules/astal/src/icons/README.md @@ -0,0 +1,2 @@ +Icons from https://glyphs.fyi with manually set stroke-width @ ~10 and color @ #000000, common iconnames might need to be renamed to avoid conflicts. +``` \ No newline at end of file diff --git a/modules/astal/src/icons/chrome-custom-symbolic.svg b/modules/astal/src/icons/chrome-custom-symbolic.svg new file mode 100644 index 0000000..00f3396 --- /dev/null +++ b/modules/astal/src/icons/chrome-custom-symbolic.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/modules/astal/src/icons/circle-filled-symbolic.svg b/modules/astal/src/icons/circle-filled-symbolic.svg new file mode 100644 index 0000000..256bdb6 --- /dev/null +++ b/modules/astal/src/icons/circle-filled-symbolic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/astal/src/icons/circle-symbolic.svg b/modules/astal/src/icons/circle-symbolic.svg new file mode 100644 index 0000000..f0525eb --- /dev/null +++ b/modules/astal/src/icons/circle-symbolic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/astal/src/icons/firefox-custom-symbolic.svg b/modules/astal/src/icons/firefox-custom-symbolic.svg new file mode 100644 index 0000000..88636ff --- /dev/null +++ b/modules/astal/src/icons/firefox-custom-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/astal/src/icons/git-symbolic.svg b/modules/astal/src/icons/git-symbolic.svg new file mode 100644 index 0000000..5a87e99 --- /dev/null +++ b/modules/astal/src/icons/git-symbolic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/astal/src/icons/microphone-custom-symbolic.svg b/modules/astal/src/icons/microphone-custom-symbolic.svg new file mode 100644 index 0000000..11a2ab9 --- /dev/null +++ b/modules/astal/src/icons/microphone-custom-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/astal/src/icons/plus-symbolic.svg b/modules/astal/src/icons/plus-symbolic.svg new file mode 100644 index 0000000..8e1870a --- /dev/null +++ b/modules/astal/src/icons/plus-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/astal/src/icons/python-symbolic.svg b/modules/astal/src/icons/python-symbolic.svg new file mode 100644 index 0000000..369a48c --- /dev/null +++ b/modules/astal/src/icons/python-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/astal/src/icons/star-filled-symbolic.svg b/modules/astal/src/icons/star-filled-symbolic.svg new file mode 100644 index 0000000..f91808f --- /dev/null +++ b/modules/astal/src/icons/star-filled-symbolic.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/astal/src/icons/terminal-symbolic.svg b/modules/astal/src/icons/terminal-symbolic.svg new file mode 100644 index 0000000..188070c --- /dev/null +++ b/modules/astal/src/icons/terminal-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/modules/astal/src/icons/vscode-custom-symbolic.svg b/modules/astal/src/icons/vscode-custom-symbolic.svg new file mode 100644 index 0000000..ae0d41b --- /dev/null +++ b/modules/astal/src/icons/vscode-custom-symbolic.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/modules/astal/src/package-lock.json b/modules/astal/src/package-lock.json new file mode 100644 index 0000000..7d214d7 --- /dev/null +++ b/modules/astal/src/package-lock.json @@ -0,0 +1,188 @@ +{ + "name": "astal-shell", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "astal-shell", + "dependencies": { + "astal": "/usr/share/astal/gjs" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4" + } + }, + "../../../../usr/share/astal/gjs": { + "name": "astal", + "license": "LGPL-2.1" + }, + "node_modules/@biomejs/biome": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/astal": { + "resolved": "../../../../usr/share/astal/gjs", + "link": true + } + } +} diff --git a/modules/astal/src/package.json b/modules/astal/src/package.json new file mode 100644 index 0000000..506fdcc --- /dev/null +++ b/modules/astal/src/package.json @@ -0,0 +1,9 @@ +{ + "name": "astal-shell", + "dependencies": { + "astal": "/usr/share/astal/gjs" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4" + } +} diff --git a/modules/astal/src/src/bar/Bar.scss b/modules/astal/src/src/bar/Bar.scss new file mode 100644 index 0000000..9ca08e0 --- /dev/null +++ b/modules/astal/src/src/bar/Bar.scss @@ -0,0 +1,88 @@ +@use "../variables.scss" as *; + +.Bar { + color: $fg; + background-color: $bg; + border-bottom: 2px solid $accent; + font-size: $font-large; +} + +.SecondaryBar { + color: $fg; + background-color: $bg; + font-size: $font-large; + transition: opacity $transition, border-color $transition; + &.left { + border-left: 2px solid $accent; + border-bottom: 2px solid $accent; + border-bottom-left-radius: $radius; + } + &.right { + border-right: 2px solid $accent; + border-bottom: 2px solid $accent; + border-bottom-right-radius: $radius; + } + &.top { + border-right: 2px solid $accent; + border-top: 2px solid $accent; + border-top-right-radius: $radius; + } + &.bottom { + border-right: 2px solid $accent; + border-bottom: 2px solid $accent; + border-bottom-right-radius: $radius; + } + &.inactive { + opacity: 0.7; + border-color: transparent; + } +} + +.Time { + font-weight: bold; +} + +.SysTray { + button { + all: unset; + &:hover { + all: unset; + } + } + + .menu, modelbutton { + background-color: $bg; + transition: background-color $transition; + } + modelbutton:hover { + background-color: $bg-alt; + } +} + +.FocusedClient { + color: $accent; + padding: 0 5px; +} + +.Slider { + highlight { + background-color: $accent; + border-radius: $radius; + } + trough { + background-color: $bg-alt; + border-radius: $radius; + min-height: 5px; + min-width: 80px; + } +} + +.Right > * { + border-left: 2px solid $accent; + padding: 4px 15px; +} + +.Left > * { + border-right: 2px solid $accent; + padding: 4px 15px; +} diff --git a/modules/astal/src/src/bar/Bar.tsx b/modules/astal/src/src/bar/Bar.tsx new file mode 100644 index 0000000..e97e52a --- /dev/null +++ b/modules/astal/src/src/bar/Bar.tsx @@ -0,0 +1,122 @@ +import Hyprland from "gi://AstalHyprland"; +import Tray from "gi://AstalTray"; +import Pango from "gi://Pango?version=1.0"; +import { GLib, Variable, bind } from "astal"; +import { App, Astal, type Gdk, Gtk } from "astal/gtk4"; +import config from "../config"; +import { getHyprlandMonitor } from "../utils/monitors"; +import { Calendar } from "../widgets"; +import { connectDropdown } from "./sections/Dropdown"; +import Media from "./sections/Media"; +import { Playback } from "./sections/Playback"; +import { Workspaces } from "./sections/Workspace"; + +function SysTray() { + const tray = Tray.get_default(); + const item = bind(tray, "items").as((items) => + items.filter((item) => config.tray.ignore.every((re) => !re.test(item.id))), + ); + + return ( + items.length > 0)} + > + {bind(item).as((items) => + items.map((item) => ( + ["dbusmenu", ag])} + menuModel={bind(item, "menuModel")} + > + + + )), + )} + + ); +} + +function FocusedClient() { + const hypr = Hyprland.get_default(); + const focused = bind(hypr, "focusedClient"); + + return ( + + {focused.as((client) => { + if (!client) { + return; + } + return ( + + ); +} + +function Time(props: { monitor: Gdk.Monitor }) { + const datetime = Variable(GLib.DateTime.new_now_local()).poll( + 1000, + () => { + return GLib.DateTime.new_now_local(); + }, + ); + const date = bind(datetime).as((dt) => dt.format("%Y-%m-%d") ?? ""); + const time = bind(datetime).as((dt) => dt.format("%H:%M") ?? ""); + + return ( + { + connectDropdown(self, , props.monitor); + }} + > + + ); +} + +export default function Bar(monitor: Gdk.Monitor) { + const { TOP, LEFT, RIGHT } = Astal.WindowAnchor; + const hyprlandMonitor = getHyprlandMonitor(monitor); + + return ( + + + + + + + + + + + + + + + ); +} diff --git a/modules/astal/src/src/bar/SecondaryBar.tsx b/modules/astal/src/src/bar/SecondaryBar.tsx new file mode 100644 index 0000000..744f90c --- /dev/null +++ b/modules/astal/src/src/bar/SecondaryBar.tsx @@ -0,0 +1,106 @@ +import Hyprland from "gi://AstalHyprland"; +import { App } from "astal/gtk4"; +import { Astal, type Gdk, Gtk } from "astal/gtk4"; +import { getHyprlandMonitor } from "../utils/monitors"; +import { Workspaces } from "./sections/Workspace"; +import { bind, type Binding, Variable } from "astal"; + +const hypr = Hyprland.get_default(); + +interface AddWorkspaceButtonProps { + show: Binding; + cssClasses: string[]; +} + +const AddWorkspaceButton = ({ show, cssClasses }: AddWorkspaceButtonProps) => { + return ( + + + + ); +}; + +export default function SecondaryBar( + monitor: Gdk.Monitor, + relation: "top" | "bottom" | "left" | "right", +) { + const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor; + const hyprlandMonitor = getHyprlandMonitor(monitor); + + const anchor = { + top: BOTTOM | LEFT, + left: TOP | RIGHT, + right: TOP | LEFT, + bottom: TOP | LEFT, + }[relation]; + + const cssClasses = { + top: ["SecondaryBar", "top"], + left: ["SecondaryBar", "left"], + right: ["SecondaryBar", "right"], + bottom: ["SecondaryBar", "bottom"], + }[relation]; + + const alignment = { + top: Gtk.Align.START, + left: Gtk.Align.END, + right: Gtk.Align.START, + bottom: Gtk.Align.START, + }[relation]; + + const showAddWorkspaceButton = Variable(false); + const monitorFocused = bind(hypr, "focusedMonitor").as( + (fm) => fm === hyprlandMonitor, + ); + + return ( + showAddWorkspaceButton.set(true)} + onHoverLeave={() => showAddWorkspaceButton.set(false)} + > + + x ? cssClasses : [...cssClasses, "inactive"], + )} + > + {relation === "left" ? ( + + ) : null} + + {relation !== "left" ? ( + + ) : null} + + + ); +} diff --git a/modules/astal/src/src/bar/sections/Dropdown.scss b/modules/astal/src/src/bar/sections/Dropdown.scss new file mode 100644 index 0000000..c2c6972 --- /dev/null +++ b/modules/astal/src/src/bar/sections/Dropdown.scss @@ -0,0 +1,10 @@ +@use "../../variables.scss" as *; + +.Dropdown { + padding: 15px; + background-color: $bg; + border: 2px solid $accent; + border-top: 0; + border-bottom-left-radius: $radius; + border-bottom-right-radius: $radius; +} diff --git a/modules/astal/src/src/bar/sections/Dropdown.tsx b/modules/astal/src/src/bar/sections/Dropdown.tsx new file mode 100644 index 0000000..43fda2b --- /dev/null +++ b/modules/astal/src/src/bar/sections/Dropdown.tsx @@ -0,0 +1,133 @@ +import { type Binding, Variable, bind } from "astal"; +import { App, Astal, type Gdk, Gtk, hook } from "astal/gtk4"; +import { cancelTimeout, cancelableTimeout } from "../../utils/timeout"; + +const ANIMATION_DURATION = 500; + +/** + * Calculate the offset and width of the parent widget + * + * @returns [offset, width] + */ +const calculateParentSize = (widget: Gtk.Widget): [number, number] => { + const [_, x, __] = widget.translate_coordinates(widget.root, 0, 0); + + // These properties are apparently deprecated, but I can't find a better way to get them + const padding = widget.get_style_context().get_padding().left; + const margin = widget.get_style_context().get_margin().left; + const borderWidth = widget.get_style_context().get_border().left; + + const offset = x - padding - margin - borderWidth; + + // Get allocated width doesn't include border width, so we have to add it back + const width = widget.get_allocated_width() + borderWidth; + + return [offset, width]; +}; + +interface ConnectDropdownProps { + fullWidth?: boolean; +} + +export function connectDropdown( + widget: Gtk.Widget, + child: JSX.Element | Binding | null, + gdkmonitor: Gdk.Monitor, + options: ConnectDropdownProps = {}, +) { + const hoverTrigger = Variable(false); + const hoverOverlay = Variable(false); + const offsetX = Variable(0); + const width = Variable(-1); + const isHovering = Variable.derive( + [hoverTrigger, hoverOverlay], + // (trigger, overlay) => trigger || overlay, + (trigger, overlay) => trigger || overlay, + ); + + const box = ( + hoverOverlay.set(true)} + onHoverLeave={() => hoverOverlay.set(false)} + > + {child} + + ); + + const dropdown = ( + + { + bind(self, "child_revealed").subscribe(is_revealed => { + if (!is_revealed) { + dropdown.hide(); + } + }); + }} + > + {box} + + + ) as Gtk.Window; + + isHovering.subscribe((hovering) => { + if (hovering) { + dropdown.show(); + (dropdown.get_first_child() as Gtk.Revealer).set_reveal_child(true); + } else { + (dropdown.get_first_child() as Gtk.Revealer).set_reveal_child(false); + } + }); + + const hoverController = new Gtk.EventControllerMotion(); + widget.add_controller(hoverController); + + hoverController.connect("enter", () => { + cancelableTimeout( + () => { + const [offset, parentWidth] = calculateParentSize(widget); + + if (options.fullWidth) { + width.set(parentWidth); + } + const dropdownWidth = + (box.get_preferred_size()[1]?.width ?? 0) - offsetX.get(); + + const centerOffset = dropdownWidth / 2 - parentWidth / 2; + const totalOffset = offset - centerOffset; + + // Ensure the dropdown doesn't go off the screen + const maxOffset = gdkmonitor.get_geometry().width - dropdownWidth; + offsetX.set(Math.max(Math.min(totalOffset, maxOffset), 0)); + hoverTrigger.set(true); + }, + "showDropdown", + 100, + ); + }); + + hoverController.connect("leave", () => { + cancelTimeout("showDropdown"); + hoverTrigger.set(false); + }); + + widget.connect("destroy", () => { + isHovering.drop(); + hoverOverlay.drop(); + hoverTrigger.drop(); + offsetX.drop(); + widget.remove_controller(hoverController); + }); +} diff --git a/modules/astal/src/src/bar/sections/Media.scss b/modules/astal/src/src/bar/sections/Media.scss new file mode 100644 index 0000000..d183c6a --- /dev/null +++ b/modules/astal/src/src/bar/sections/Media.scss @@ -0,0 +1,37 @@ +@use "../../variables.scss" as *; + +.MediaDropdown { + .MediaCover { + border-radius: $radius; + margin-bottom: 5px; + } + + .MediaArtist { + color: $fg-alt; + } + + .MediaAlbum { + color: $fg-alt; + font-size: 12px; + } + + .Slider { + margin-top: 10px; + margin-bottom: 10px; + } + + separator { + margin-top: 10px; + margin-bottom: 10px; + } + + .MediaOther { + margin-top: 10px; + + button.active { + background-color: $accent; + border-color: $accent; + color: $bg; + } + } +} diff --git a/modules/astal/src/src/bar/sections/Media.tsx b/modules/astal/src/src/bar/sections/Media.tsx new file mode 100644 index 0000000..4384581 --- /dev/null +++ b/modules/astal/src/src/bar/sections/Media.tsx @@ -0,0 +1,387 @@ +import Mpris from "gi://AstalMpris"; +import Pango from "gi://Pango?version=1.0"; +import { type Binding, Variable, bind } from "astal"; +import type { Subscribable } from "astal/binding"; +import { type Gdk, Gtk } from "astal/gtk4"; +import { hasIcon } from "../../utils/gtk"; +import { Expander, Separator } from "../../widgets"; +import { connectDropdown } from "./Dropdown"; + +const mpris = Mpris.get_default(); +const MARQUEE_LENGTH = 30; + +interface MprisStatus { + status: Mpris.PlaybackStatus; + lastPlayed: number; + canControl: boolean; +} + +class ActiveMediaDetector implements Subscribable { + #userOverride: string | undefined; + #players: { [busName: string]: MprisStatus } = {}; + #listenerSignal = new Map(); + #active = Variable(undefined); + + #updateActive() { + const busName = Object.entries(this.#players) + .filter(([, status]) => { + // Don't consider players that are stopped or can't be controlled + if (status.status === Mpris.PlaybackStatus.STOPPED) { + return false; + } + return status.canControl; + }) + .sort(([aName, a], [bName, b]) => { + if (aName === this.#userOverride) { + return -1; + } + + if (bName === this.#userOverride) { + return 1; + } + + if ( + a.status === Mpris.PlaybackStatus.PLAYING && + b.status !== Mpris.PlaybackStatus.PLAYING + ) { + return -1; + } + + if ( + b.status === Mpris.PlaybackStatus.PLAYING && + a.status !== Mpris.PlaybackStatus.PLAYING + ) { + return 1; + } + + return b.lastPlayed - a.lastPlayed; + })[0]?.[0]; + const player = busName + ? mpris.get_players().find((player) => player.bus_name === busName) + : undefined; + this.#active.set(player); + } + + #handleUpdate(player: Mpris.Player) { + const lastStatus = this.#players[player.bus_name]?.status; + let lastPlayed = this.#players[player.bus_name]?.lastPlayed ?? -1; + + // If the player is playing (or was just playing), update the last played time + if ( + player.playback_status === Mpris.PlaybackStatus.PLAYING || + lastStatus === Mpris.PlaybackStatus.PLAYING + ) { + lastPlayed = Date.now(); + } + + this.#players[player.bus_name] = { + status: player.playback_status, + lastPlayed: lastPlayed, + canControl: player.can_control, + }; + this.#updateActive(); + } + + #connect(player: Mpris.Player) { + const signal = player.connect("notify::playback-status", () => { + this.#handleUpdate(player); + }); + + this.#listenerSignal.set(player, signal); + } + + #disconnect(player: Mpris.Player) { + const signal = this.#listenerSignal.get(player); + if (signal) { + player.disconnect(signal); + this.#listenerSignal.delete(player); + } + } + + constructor() { + for (const player of mpris.players) { + this.#handleUpdate(player); + this.#connect(player); + } + + mpris.connect("player-added", (_, player) => { + this.#handleUpdate(player); + this.#connect(player); + }); + + mpris.connect("player-closed", (_, player) => { + delete this.#players[player.bus_name]; + this.#disconnect(player); + }); + } + + get override() { + return this.#userOverride; + } + set override(busName: string | undefined) { + this.#userOverride = busName; + this.#updateActive(); + } + + get(): Mpris.Player | undefined { + return this.#active.get(); + } + + subscribe(callback: (value: Mpris.Player | undefined) => void): () => void { + return this.#active.subscribe(callback); + } +} + +const formatTime = (time: number) => { + const hours = Math.floor(time / 3600); + const minutes = Math.floor((time % 3600) / 60) + .toString() + .padStart(2, "0"); + const seconds = Math.floor(time % 60) + .toString() + .padStart(2, "0"); + return `${hours > 0 ? `${hours}:` : ""}${minutes}:${seconds}`; +}; + +interface MediaDropdownProps { + activePlayer: Binding; + onOverride: (busName: string) => void; +} + +function MediaDropdown({ activePlayer, onOverride }: MediaDropdownProps) { + const allPlayers = bind(mpris, "players"); + + return ( + + {activePlayer.as((player) => { + if (!player) { + return null; + } + + return ( + <> + + + ); +} + +interface MediaProps { + monitor: Gdk.Monitor; +} + +export default function Media({ monitor }: MediaProps) { + const activeMedia = new ActiveMediaDetector(); + const activePlayer = bind(activeMedia); + + return ( + + connectDropdown( + self, + { + if (activeMedia.override === busName) { + activeMedia.override = undefined; + } else { + activeMedia.override = busName; + } + }} + />, + monitor, + { fullWidth: true }, + ) + } + visible={activePlayer.as(Boolean)} + > + {activePlayer.as((player) => { + if (!player) { + return; + } + + const icon = bind(player, "entry").as((e) => + hasIcon(e) ? e : "audio-x-generic-symbolic", + ); + + const marqueeOffset = Variable(0).poll(100, (offset) => { + return offset + 1; + }); + + // show marquee for the first and last 10 seconds of a song + const showMarquee = Variable.derive( + [ + bind(player, "length"), + bind(player, "position"), + bind(player, "playbackStatus"), + ], + (length, position, status) => { + if (status !== Mpris.PlaybackStatus.PLAYING) { + return false; + } + return position < 10 || length - position < 10; + }, + ); + showMarquee.subscribe((show) => { + if (show) { + marqueeOffset.poll(100, (offset) => { + return offset + 1; + }); + } else { + marqueeOffset.stopPoll(); + } + }); + bind(player, "title").subscribe(() => marqueeOffset.set(0)); + + const marquee = Variable.derive( + [bind(player, "title"), bind(player, "artist"), bind(marqueeOffset)], + (title, artist, mo) => { + const line = `${title} - ${artist} `; + if (line.length <= MARQUEE_LENGTH) { + // center the text + return line + .padStart(20 + line.length / 2, " ") + .padEnd(MARQUEE_LENGTH, " "); + } + const offset = mo % line.length; + return (line + line).slice(offset, offset + MARQUEE_LENGTH); + }, + ); + + return ( + <> + + + show ? "marquee" : "progress", + )} + transitionType={Gtk.StackTransitionType.CROSSFADE} + transitionDuration={200} + > + + + + + ); + })} + + ); +} diff --git a/modules/astal/src/src/bar/sections/Playback.scss b/modules/astal/src/src/bar/sections/Playback.scss new file mode 100644 index 0000000..c4dfeca --- /dev/null +++ b/modules/astal/src/src/bar/sections/Playback.scss @@ -0,0 +1,28 @@ +@use "../../variables.scss" as *; + +@keyframes pulse { + 0% { + background-color: $accent; + } + 50% { + background-color: $bg-alt; + } + 100% { + background-color: $accent; + } + } + +.PlaybackDropdown { + .no-streams { + color: $muted; + font-size: 14px; + } +} + +.Playback { + .recording { + background-color: $accent; + border-radius: $radius; + animation: pulse 1s 10; + } +} \ No newline at end of file diff --git a/modules/astal/src/src/bar/sections/Playback.tsx b/modules/astal/src/src/bar/sections/Playback.tsx new file mode 100644 index 0000000..c6cda3f --- /dev/null +++ b/modules/astal/src/src/bar/sections/Playback.tsx @@ -0,0 +1,250 @@ +import Wp from "gi://AstalWp"; +import { Variable, bind } from "astal"; +import type { Binding, Subscribable } from "astal/binding"; +import { Gtk, type Gdk } from "astal/gtk4"; +import { hasIcon } from "../../utils/gtk"; +import { Expander, FlowBox, Separator } from "../../widgets"; +import { connectDropdown } from "./Dropdown"; +import Pango from "gi://Pango?version=1.0"; +import { Box } from "astal/gtk4/widget"; + +interface PlaybackEndpointProps { + endpoint: Wp.Endpoint; + visible?: Binding; +} + +function PlaybackEndpoint({ endpoint, visible }: PlaybackEndpointProps) { + const name = Variable.derive( + [bind(endpoint, "description"), bind(endpoint, "name")], + (description, name) => name || description || "Unknown", + ); + + const defaultable = Variable.derive( + [bind(endpoint, "is_default"), bind(endpoint, "media_class")], + (isDefault, mediaClass) => + !isDefault && + [Wp.MediaClass.AUDIO_MICROPHONE, Wp.MediaClass.AUDIO_SPEAKER].includes( + mediaClass, + ), + ); + + return ( + { + name.drop(); + defaultable.drop(); + }} + visible={visible} + > + + + + + { + endpoint.set_volume(value); + }} + /> + + + ); +} + +function PlaybackDropdown({ audioDevices }: { audioDevices: Wp.Audio }) { + return ( + + + ); +} + +export function Playback({ monitor }: { monitor: Gdk.Monitor }) { + const audioDevices = Wp.get_default()?.get_audio?.(); + if (!audioDevices) { + return