commit 96895ec3aa92cffe625c39b9d774c9144fb7e961 Author: EdenQwQ Date: Sat Mar 1 22:35:08 2025 +0800 init diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..9198e5a --- /dev/null +++ b/flake.lock @@ -0,0 +1,1051 @@ +{ + "nodes": { + "base16": { + "inputs": { + "fromYaml": "fromYaml" + }, + "locked": { + "lastModified": 1732200724, + "narHash": "sha256-+R1BH5wHhfnycySb7Sy5KbYEaTJZWm1h+LW1OtyhiTs=", + "owner": "SenchoPens", + "repo": "base16.nix", + "rev": "153d52373b0fb2d343592871009a286ec8837aec", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "base16.nix", + "type": "github" + } + }, + "base16-fish": { + "flake": false, + "locked": { + "lastModified": 1622559957, + "narHash": "sha256-PebymhVYbL8trDVVXxCvZgc0S5VxI7I1Hv4RMSquTpA=", + "owner": "tomyun", + "repo": "base16-fish", + "rev": "2f6dd973a9075dabccd26f1cded09508180bf5fe", + "type": "github" + }, + "original": { + "owner": "tomyun", + "repo": "base16-fish", + "type": "github" + } + }, + "base16-helix": { + "flake": false, + "locked": { + "lastModified": 1736852337, + "narHash": "sha256-esD42YdgLlEh7koBrSqcT7p2fsMctPAcGl/+2sYJa2o=", + "owner": "tinted-theming", + "repo": "base16-helix", + "rev": "03860521c40b0b9c04818f2218d9cc9efc21e7a5", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-helix", + "type": "github" + } + }, + "base16-vim": { + "flake": false, + "locked": { + "lastModified": 1732806396, + "narHash": "sha256-e0bpPySdJf0F68Ndanwm+KWHgQiZ0s7liLhvJSWDNsA=", + "owner": "tinted-theming", + "repo": "base16-vim", + "rev": "577fe8125d74ff456cf942c733a85d769afe58b7", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-vim", + "rev": "577fe8125d74ff456cf942c733a85d769afe58b7", + "type": "github" + } + }, + "firefox-gnome-theme": { + "flake": false, + "locked": { + "lastModified": 1739223196, + "narHash": "sha256-vAxN2f3rvl5q62gQQjZGVSvF93nAsOxntuFz+e/655w=", + "owner": "rafaelmardojai", + "repo": "firefox-gnome-theme", + "rev": "a89108e6272426f4eddd93ba17d0ea101c34fb21", + "type": "github" + }, + "original": { + "owner": "rafaelmardojai", + "repo": "firefox-gnome-theme", + "type": "github" + } + }, + "flake-compat": { + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_3": { + "inputs": { + "nixpkgs-lib": [ + "nixvim", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1738453229, + "narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_4": { + "inputs": { + "nixpkgs-lib": [ + "nur", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_5": { + "inputs": { + "nixpkgs-lib": [ + "stylix", + "nur", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-root": { + "locked": { + "lastModified": 1723604017, + "narHash": "sha256-rBtQ8gg+Dn4Sx/s+pvjdq3CB2wQNzx9XGFq/JVGCB6k=", + "owner": "srid", + "repo": "flake-root", + "rev": "b759a56851e10cb13f6b8e5698af7b59c44be26e", + "type": "github" + }, + "original": { + "owner": "srid", + "repo": "flake-root", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": [ + "stylix", + "systems" + ] + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "fromYaml": { + "flake": false, + "locked": { + "lastModified": 1731966426, + "narHash": "sha256-lq95WydhbUTWig/JpqiB7oViTcHFP8Lv41IGtayokA8=", + "owner": "SenchoPens", + "repo": "fromYaml", + "rev": "106af9e2f715e2d828df706c386a685698f3223b", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "fromYaml", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": [ + "stylix", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "stylix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737465171, + "narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "stylix", + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gnome-shell": { + "flake": false, + "locked": { + "lastModified": 1732369855, + "narHash": "sha256-JhUWbcYPjHO3Xs3x9/Z9RuqXbcp5yhPluGjwsdE2GMg=", + "owner": "GNOME", + "repo": "gnome-shell", + "rev": "dadd58f630eeea41d645ee225a63f719390829dc", + "type": "github" + }, + "original": { + "owner": "GNOME", + "ref": "47.2", + "repo": "gnome-shell", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1740699498, + "narHash": "sha256-r9hkKzX99CGiP1ZqH0e+SWKK4CMsRNRLyotuwrUjhTI=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "b71edac7a3167026aabea82a54d08b1794088c21", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "master", + "repo": "home-manager", + "type": "github" + } + }, + "home-manager_2": { + "inputs": { + "nixpkgs": [ + "stylix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1740347597, + "narHash": "sha256-st5q9egkPGz8TUcVVlIQX7y6G3AzHob+6M963bwVq74=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "12e26a74e5eb1a31e13daaa08858689e25ebd449", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "ixx": { + "inputs": { + "flake-utils": [ + "nixvim", + "nuschtosSearch", + "flake-utils" + ], + "nixpkgs": [ + "nixvim", + "nuschtosSearch", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729958008, + "narHash": "sha256-EiOq8jF4Z/zQe0QYVc3+qSKxRK//CFHMB84aYrYGwEs=", + "owner": "NuschtOS", + "repo": "ixx", + "rev": "9fd01aad037f345350eab2cd45e1946cc66da4eb", + "type": "github" + }, + "original": { + "owner": "NuschtOS", + "ref": "v0.0.6", + "repo": "ixx", + "type": "github" + } + }, + "nh": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1740563087, + "narHash": "sha256-ILEarFQp89V6RWr5GRU/ip9dB1SEqUFQGs1QMPrk9TQ=", + "owner": "viperML", + "repo": "nh", + "rev": "1b25d633376f50139f7b8fe314c8914711d44b42", + "type": "github" + }, + "original": { + "owner": "viperML", + "repo": "nh", + "type": "github" + } + }, + "nil": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1732053863, + "narHash": "sha256-DCIVdlb81Fct2uwzbtnawLBC/U03U2hqx8trqTJB7WA=", + "owner": "oxalica", + "repo": "nil", + "rev": "2e24c9834e3bb5aa2a3701d3713b43a6fb106362", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "nil", + "type": "github" + } + }, + "niri": { + "inputs": { + "niri-stable": "niri-stable", + "niri-unstable": "niri-unstable", + "nixpkgs": "nixpkgs_2", + "nixpkgs-stable": "nixpkgs-stable", + "xwayland-satellite-stable": "xwayland-satellite-stable", + "xwayland-satellite-unstable": "xwayland-satellite-unstable" + }, + "locked": { + "lastModified": 1740690416, + "narHash": "sha256-ZVyJvL/sEkBuxNh1+Y5K3XXzYowIz5UUyOqrzK2rkdc=", + "owner": "sodiboo", + "repo": "niri-flake", + "rev": "18a5c6286802a8ef1109760d61740b6f321c810c", + "type": "github" + }, + "original": { + "owner": "sodiboo", + "repo": "niri-flake", + "type": "github" + } + }, + "niri-stable": { + "flake": false, + "locked": { + "lastModified": 1740117926, + "narHash": "sha256-mTTHA0RAaQcdYe+9A3Jx77cmmyLFHmRoZdd8RpWa+m8=", + "owner": "YaLTeR", + "repo": "niri", + "rev": "b94a5db8790339cf9134873d8b490be69e02ac71", + "type": "github" + }, + "original": { + "owner": "YaLTeR", + "ref": "v25.02", + "repo": "niri", + "type": "github" + } + }, + "niri-unstable": { + "flake": false, + "locked": { + "lastModified": 1740683787, + "narHash": "sha256-Q3ZqUR1xVG9MuHxInsLfqytgqCZKICYpWDIz8L11av8=", + "owner": "YaLTeR", + "repo": "niri", + "rev": "311f3be5d861bfb12ee8b3d7241bd1d1fcb5110b", + "type": "github" + }, + "original": { + "owner": "YaLTeR", + "repo": "niri", + "type": "github" + } + }, + "nixd": { + "inputs": { + "flake-parts": "flake-parts_2", + "flake-root": "flake-root", + "nixpkgs": [ + "nixpkgs" + ], + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1739259093, + "narHash": "sha256-pltdsmg4oh1AVNrMbKUgx1VPbndVdCGID5mrBmRIdVI=", + "owner": "nix-community", + "repo": "nixd", + "rev": "065dcb4cb2f8269d6d15d2b2491a79cff47f9550", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixd", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1735563628, + "narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1738452942, + "narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" + } + }, + "nixpkgs-lib_2": { + "locked": { + "lastModified": 1733096140, + "narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1740603184, + "narHash": "sha256-t+VaahjQAWyA+Ctn2idyo1yxRIYpaDxMgHkgCNiMJa4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1740560979, + "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5135c59491985879812717f4c9fea69604e7f26f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1740560979, + "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5135c59491985879812717f4c9fea69604e7f26f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1740560979, + "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5135c59491985879812717f4c9fea69604e7f26f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1740560979, + "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5135c59491985879812717f4c9fea69604e7f26f", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixvim": { + "inputs": { + "flake-parts": "flake-parts_3", + "nixpkgs": [ + "nixpkgs" + ], + "nuschtosSearch": "nuschtosSearch" + }, + "locked": { + "lastModified": 1740520037, + "narHash": "sha256-TpZMYjOre+6GhKDVHFwoW2iBWqpNQppQTuqIAo+OBV8=", + "owner": "nix-community", + "repo": "nixvim", + "rev": "6f8d8f7aee84f377f52c8bb58385015f9168a666", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixvim", + "type": "github" + } + }, + "nur": { + "inputs": { + "flake-parts": "flake-parts_4", + "nixpkgs": "nixpkgs_4", + "treefmt-nix": "treefmt-nix_2" + }, + "locked": { + "lastModified": 1740715395, + "narHash": "sha256-K5RjsCwS60wO9Y999R2q4J1KH/pZcxOGJZmdvucs8v4=", + "owner": "nix-community", + "repo": "NUR", + "rev": "0ed1a467447a04a60cdc6444de0dedd9adc55952", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "NUR", + "type": "github" + } + }, + "nur_2": { + "inputs": { + "flake-parts": "flake-parts_5", + "nixpkgs": [ + "stylix", + "nixpkgs" + ], + "treefmt-nix": "treefmt-nix_3" + }, + "locked": { + "lastModified": 1740408283, + "narHash": "sha256-2xECnhgF3MU9YjmvOkrRp8wRFo2OjjewgCtlfckhL5s=", + "owner": "nix-community", + "repo": "NUR", + "rev": "496a4a11162bdffb9a7b258942de138873f019f7", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "NUR", + "type": "github" + } + }, + "nuschtosSearch": { + "inputs": { + "flake-utils": "flake-utils_2", + "ixx": "ixx", + "nixpkgs": [ + "nixvim", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1738508923, + "narHash": "sha256-4DaDrQDAIxlWhTjH6h/+xfG05jt3qDZrZE/7zDLQaS4=", + "owner": "NuschtOS", + "repo": "search", + "rev": "86e2038290859006e05ca7201425ea5b5de4aecb", + "type": "github" + }, + "original": { + "owner": "NuschtOS", + "repo": "search", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "home-manager": "home-manager", + "nh": "nh", + "nil": "nil", + "niri": "niri", + "nixd": "nixd", + "nixpkgs": "nixpkgs_3", + "nixpkgs-unstable": "nixpkgs-unstable", + "nixvim": "nixvim", + "nur": "nur", + "stylix": "stylix" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nil", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1731983527, + "narHash": "sha256-JECaBgC0pQ91Hq3W4unH6K9to8s2Zl2sPNu7bLOv4ek=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "71287228d96e9568e1e70c6bbfa3f992d145947b", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "stylix": { + "inputs": { + "base16": "base16", + "base16-fish": "base16-fish", + "base16-helix": "base16-helix", + "base16-vim": "base16-vim", + "firefox-gnome-theme": "firefox-gnome-theme", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_3", + "git-hooks": "git-hooks", + "gnome-shell": "gnome-shell", + "home-manager": "home-manager_2", + "nixpkgs": [ + "nixpkgs" + ], + "nur": "nur_2", + "systems": "systems_3", + "tinted-foot": "tinted-foot", + "tinted-kitty": "tinted-kitty", + "tinted-schemes": "tinted-schemes", + "tinted-tmux": "tinted-tmux", + "tinted-zed": "tinted-zed" + }, + "locked": { + "lastModified": 1740644467, + "narHash": "sha256-i2ArXwncE2OmneLBllo5OlpLB2UsXU5JX+T+7or5OX4=", + "owner": "danth", + "repo": "stylix", + "rev": "e7c09d206680e6fe6771e1ac9a83515313feaf95", + "type": "github" + }, + "original": { + "owner": "danth", + "repo": "stylix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "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" + } + }, + "tinted-foot": { + "flake": false, + "locked": { + "lastModified": 1726913040, + "narHash": "sha256-+eDZPkw7efMNUf3/Pv0EmsidqdwNJ1TaOum6k7lngDQ=", + "owner": "tinted-theming", + "repo": "tinted-foot", + "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-foot", + "rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4", + "type": "github" + } + }, + "tinted-kitty": { + "flake": false, + "locked": { + "lastModified": 1716423189, + "narHash": "sha256-2xF3sH7UIwegn+2gKzMpFi3pk5DlIlM18+vj17Uf82U=", + "owner": "tinted-theming", + "repo": "tinted-kitty", + "rev": "eb39e141db14baef052893285df9f266df041ff8", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-kitty", + "rev": "eb39e141db14baef052893285df9f266df041ff8", + "type": "github" + } + }, + "tinted-schemes": { + "flake": false, + "locked": { + "lastModified": 1740351358, + "narHash": "sha256-Hdk850xgAd3DL8KX0AbyU7tC834d3Lej1jOo3duWiOA=", + "owner": "tinted-theming", + "repo": "schemes", + "rev": "a1bc2bd89e693e7e3f5764cfe8114e2ae150e184", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "schemes", + "type": "github" + } + }, + "tinted-tmux": { + "flake": false, + "locked": { + "lastModified": 1740272597, + "narHash": "sha256-/etfUV3HzAaLW3RSJVwUaW8ULbMn3v6wbTlXSKbcoWQ=", + "owner": "tinted-theming", + "repo": "tinted-tmux", + "rev": "b6c7f46c8718cc484f2db8b485b06e2a98304cd0", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "tinted-tmux", + "type": "github" + } + }, + "tinted-zed": { + "flake": false, + "locked": { + "lastModified": 1725758778, + "narHash": "sha256-8P1b6mJWyYcu36WRlSVbuj575QWIFZALZMTg5ID/sM4=", + "owner": "tinted-theming", + "repo": "base16-zed", + "rev": "122c9e5c0e6f27211361a04fae92df97940eccf9", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-zed", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixd", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1734704479, + "narHash": "sha256-MMi74+WckoyEWBRcg/oaGRvXC9BVVxDZNRMpL+72wBI=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "65712f5af67234dad91a5a4baee986a8b62dbf8f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { + "inputs": { + "nixpkgs": [ + "nur", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733222881, + "narHash": "sha256-JIPcz1PrpXUCbaccEnrcUS8jjEb/1vJbZz5KkobyFdM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "49717b5af6f80172275d47a418c9719a31a78b53", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_3": { + "inputs": { + "nixpkgs": [ + "stylix", + "nur", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733222881, + "narHash": "sha256-JIPcz1PrpXUCbaccEnrcUS8jjEb/1vJbZz5KkobyFdM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "49717b5af6f80172275d47a418c9719a31a78b53", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "xwayland-satellite-stable": { + "flake": false, + "locked": { + "lastModified": 1739246919, + "narHash": "sha256-/hBM43/Gd0/tW+egrhlWgOIISeJxEs2uAOIYVpfDKeU=", + "owner": "Supreeeme", + "repo": "xwayland-satellite", + "rev": "44590a416d4a3e8220e19e29e0b6efe64a80315d", + "type": "github" + }, + "original": { + "owner": "Supreeeme", + "ref": "v0.5.1", + "repo": "xwayland-satellite", + "type": "github" + } + }, + "xwayland-satellite-unstable": { + "flake": false, + "locked": { + "lastModified": 1739246919, + "narHash": "sha256-/hBM43/Gd0/tW+egrhlWgOIISeJxEs2uAOIYVpfDKeU=", + "owner": "Supreeeme", + "repo": "xwayland-satellite", + "rev": "44590a416d4a3e8220e19e29e0b6efe64a80315d", + "type": "github" + }, + "original": { + "owner": "Supreeeme", + "repo": "xwayland-satellite", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c495b57 --- /dev/null +++ b/flake.nix @@ -0,0 +1,46 @@ +{ + description = "Eden's NixOS Flake"; + + outputs = + { + self, + nixpkgs, + ... + }@inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" ]; + imports = [ + ./hosts + { _module.args = { inherit inputs self nixpkgs; }; } + ]; + }; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; + home-manager = { + url = "github:nix-community/home-manager/master"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-parts.url = "github:hercules-ci/flake-parts"; + niri.url = "github:sodiboo/niri-flake"; + nur.url = "github:nix-community/NUR"; + stylix = { + url = "github:danth/stylix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nixvim = { + url = "github:nix-community/nixvim"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nil = { + url = "github:oxalica/nil"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nixd = { + url = "github:nix-community/nixd"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nh.url = "github:viperML/nh"; + }; +} diff --git a/home/default.nix b/home/default.nix new file mode 100644 index 0000000..2eebce3 --- /dev/null +++ b/home/default.nix @@ -0,0 +1,75 @@ +{ + pkgs, + lib, + user, + ... +}: +{ + imports = [ + ./programs + ./tweaks + ]; + + home = { + username = user; + homeDirectory = "/home/${user}"; + + packages = with pkgs; [ + # files + zip + xz + unzip + + # utils + ripgrep + zoxide + fzf + eza + fd + ]; + + activation = { + niri-transition = + lib.hm.dag.entryAfter [ "writeBoundary" ] + # bash + '' + run ${pkgs.niri-unstable}/bin/niri msg action do-screen-transition --delay-ms 1000 + ''; + reload-waybar = + lib.hm.dag.entryAfter [ "niri-transition" ] + # bash + '' + run --quiet ${pkgs.systemd}/bin/systemctl --user restart waybar.service + ''; + }; + + }; + + programs = { + git = { + enable = true; + userName = "EdenQwQ"; + userEmail = "lsahlm1eden@gmail.com"; + extraConfig = { + http = { + proxy = "http://127.0.0.1:7890"; + }; + https = { + proxy = "http://127.0.0.1:7890"; + }; + safe = { + directory = "*"; + }; + }; + }; + + nix-index = { + enable = true; + enableFishIntegration = true; + enableBashIntegration = false; + enableZshIntegration = false; + }; + + home-manager.enable = true; + }; +} diff --git a/home/programs/browser/chromium.nix b/home/programs/browser/chromium.nix new file mode 100644 index 0000000..299b8ee --- /dev/null +++ b/home/programs/browser/chromium.nix @@ -0,0 +1,6 @@ +{ + programs.chromium = { + enable = true; + commandLineArgs = [ "--enable-features=UseOzonePlatform --ozone-platform=wayland" ]; + }; +} diff --git a/home/programs/browser/default.nix b/home/programs/browser/default.nix new file mode 100644 index 0000000..857f241 --- /dev/null +++ b/home/programs/browser/default.nix @@ -0,0 +1,7 @@ +{ + imports = [ + ./firefox.nix + ./chromium.nix + ./qutebrowser.nix + ]; +} diff --git a/home/programs/browser/firefox.nix b/home/programs/browser/firefox.nix new file mode 100644 index 0000000..1c5f302 --- /dev/null +++ b/home/programs/browser/firefox.nix @@ -0,0 +1,41 @@ +let + firefox-gnome-theme = builtins.fetchGit { + url = "https://github.com/rafaelmardojai/firefox-gnome-theme.git"; + rev = "a89108e6272426f4eddd93ba17d0ea101c34fb21"; + }; +in +{ + programs.firefox = { + enable = true; + profiles.default = { + isDefault = true; + userChrome = '' + @import "${firefox-gnome-theme}/userChrome.css"; + @import "${firefox-gnome-theme}/theme/colors/dark.css"; + /* Hide Tab bar with only one Tab - [110] */ + #tabbrowser-tabs .tabbrowser-tab:only-of-type, + #tabbrowser-tabs + .tabbrowser-tab:only-of-type + + #tabbrowser-arrowscrollbox-periphery { + display: none !important; + } + #tabbrowser-tabs, + #tabbrowser-arrowscrollbox { + min-height: 0 !important; + } + /* #TabsToolbar:not(:hover) */ + #alltabs-button { + display: none !important; + } + ''; + settings = { + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; + "browser.tabs.drawInTitlebar" = true; + "svg.context-properties.content.enabled" = true; + }; + }; + }; + stylix.targets.firefox.enable = true; + stylix.targets.firefox.firefoxGnomeTheme.enable = true; + stylix.targets.firefox.profileNames = [ "default" ]; +} diff --git a/home/programs/browser/qutebrowser.nix b/home/programs/browser/qutebrowser.nix new file mode 100644 index 0000000..3845056 --- /dev/null +++ b/home/programs/browser/qutebrowser.nix @@ -0,0 +1,78 @@ +{ + nixosVersion, + homeManagerVersion, + config, + lib, + ... +}: +{ + programs.qutebrowser = { + enable = true; + keyBindings = { + normal = { + ";i" = "hint images download"; + }; + }; + searchEngines = + let + nixvimVersion = if nixosVersion == "unstable" then "" else nixosVersion; + hmOptionsVersion = + if homeManagerVersion == "master" then "master" else "release-${homeManagerVersion}"; + in + { + nixp = "https://search.nixos.org/packages?channel=${nixosVersion}&from=0&size=50&sort=relevance&type=packages&query={}"; + nixo = "https://search.nixos.org/options?channel=${nixosVersion}&size=50&sort=relevance&type=packages&query={}"; + hm = "https://home-manager-options.extranix.com/?query={}&release=${hmOptionsVersion}"; + nv = "https://nix-community.github.io/nixvim/${nixvimVersion}/?search={}"; + gh = "https://github.com/search?q={}&type=repositories"; + toki = "https://glosbe.com/en/mis_tok/{}"; + deepl = "https://www.deepl.com/translator#en/zh/{}"; + deeplzh = "https://www.deepl.com/translator#zh/en/{}"; + }; + quickmarks = { + github = "https://github.com"; + "nix packages" = "https://search.nixos.org/packages?"; + "nix options" = "https://search.nixos.org/options?"; + "home manager options" = "https://mipmip.github.io/home-manager-option-search"; + catppuccin = "https://github.com/catppuccin/catppuccin"; + gpt = "https://127.0.0.1:3000"; + bilibili = "https://bilibili.com"; + "semantic scholar" = "https://www.semanticscholar.org"; + "google scholar" = "https://scholar.google.com"; + mendeley = "https://www.mendeley.com/library"; + "sona pi toki pona" = "https://www.youtube.com/playlist?list=PLuYLhuXt4HrQIv3xnDxZqRaLfmxB2U5rJ"; + "lipu Linku" = "https://linku.la"; + }; + + settings.colors = + let + selection-background = config.lib.stylix.colors.withHashtag.base02; + in + { + completion.item.selected = { + bg = lib.mkForce selection-background; + border = { + bottom = lib.mkForce selection-background; + top = lib.mkForce selection-background; + }; + match.fg = lib.mkForce config.lib.stylix.colors.withHashtag.base0B; + }; + contextmenu.selected.bg = lib.mkForce selection-background; + statusbar.caret = { + bg = lib.mkForce selection-background; + selection.bg = lib.mkForce selection-background; + }; + tabs = { + pinned.selected = { + even.bg = lib.mkForce selection-background; + odd.bg = lib.mkForce selection-background; + }; + selected = { + even.bg = lib.mkForce selection-background; + odd.bg = lib.mkForce selection-background; + }; + }; + }; + }; + stylix.targets.qutebrowser.enable = true; +} diff --git a/home/programs/coding/default.nix b/home/programs/coding/default.nix new file mode 100644 index 0000000..17bc13d --- /dev/null +++ b/home/programs/coding/default.nix @@ -0,0 +1,11 @@ +{ config, ... }: +{ + imports = [ + ./misc.nix + ./tex.nix + ./python.nix + ./neovide.nix + ./zed.nix + ./nixvim + ]; +} diff --git a/home/programs/coding/misc.nix b/home/programs/coding/misc.nix new file mode 100644 index 0000000..b55444b --- /dev/null +++ b/home/programs/coding/misc.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + alejandra + nixfmt-rfc-style + texlab + nodePackages.prettier + ]; +} diff --git a/home/programs/coding/neovide.nix b/home/programs/coding/neovide.nix new file mode 100644 index 0000000..4df5729 --- /dev/null +++ b/home/programs/coding/neovide.nix @@ -0,0 +1,6 @@ +{ + programs.neovide = { + enable = true; + settings.neovim-bin = "nvim"; + }; +} diff --git a/home/programs/coding/nixvim/ai.nix b/home/programs/coding/nixvim/ai.nix new file mode 100644 index 0000000..097ae8a --- /dev/null +++ b/home/programs/coding/nixvim/ai.nix @@ -0,0 +1,117 @@ +{ user, ... }: +{ + programs.nixvim = { + plugins = { + copilot-lua = { + enable = true; + settings.panel.enabled = false; + settings.suggestion.enabled = false; + settings.filetypes.markdown = true; + }; + copilot-chat.enable = true; + codecompanion = { + enable = true; + settings = { + adapters.copilot.__raw = + # lua + '' + function() + return require("codecompanion.adapters").extend("copilot", { + schema = { + model = { + default = "claude-3.7-sonnet", + }, + max_tokens = { + default = 65536, + } + } + }) + end + ''; + adapters.siliconflow.__raw = + # lua + '' + function () + local siliconflow_token_file = io.open("/home/${user}/Downloads/tokens/siliconflow_token", "r") + local siliconflow_api_key = siliconflow_token_file:read() + siliconflow_token_file:close() + return require("codecompanion.adapters").extend("openai_compatible", { + name = "deepseek", + env = { + url = "https://api.siliconflow.cn", + api_key = siliconflow_api_key, + }, + schema = { + model = { + default = "Pro/deepseek-ai/DeepSeek-V3", + } + } + }) + end + ''; + adapters.gemini.__raw = + # lua + '' + function() + local gemini_token_file = io.open("/home/${user}/Downloads/gemini_token", "r") + local gemini_api_key = gemini_token_file:read() + gemini_token_file:close() + return require("codecompanion.adapters").extend("gemini", { + env = { + api_key = gemini_api_key, + } + }) + end + ''; + strategies = { + inline = { + adapter = "siliconflow"; + keymaps = { + accept_change.modes.n = "ca"; + reject_change.modes.n = "cr"; + }; + }; + chat = { + adapter = "siliconflow"; + slash_commands.__raw = # lua + '' + { + ["file"] = { + opts = { + provider = "telescope", + }, + }, + }, + { + ["buffer"] = { + opts = { + provider = "telescope", + }, + }, + }, + ''; + }; + agent.adapter = "siliconflow"; + }; + }; + }; + }; + keymaps = [ + { + mode = "n"; + key = "ca"; + action = ":CodeCompanionActions"; + } + { + mode = "n"; + key = "cc"; + action = ":CodeCompanionChat Toggle"; + } + { + mode = "n"; + key = "ci"; + action = ":CodeCompanion "; + } + ]; + }; +} diff --git a/home/programs/coding/nixvim/cmp.nix b/home/programs/coding/nixvim/cmp.nix new file mode 100644 index 0000000..a662000 --- /dev/null +++ b/home/programs/coding/nixvim/cmp.nix @@ -0,0 +1,36 @@ +{ + programs.nixvim = { + plugins.cmp = { + enable = true; + settings.sources = [ + { name = "buffer"; } + { name = "path"; } + { name = "spell"; } + { name = "emoji"; } + { name = "luasnip"; } + { name = "nvim_lsp"; } + { name = "copilot"; } + ]; + settings.mapping = { + "" = "cmp.mapping(cmp.mapping.select_next_item(), {'i', 's'})"; + "" = "cmp.mapping(cmp.mapping.select_prev_item(), {'i', 's'})"; + "" = "cmp.mapping.confirm({ select = true })"; + }; + settings.window.completion.border = [ + "╭" + "─" + "╮" + "│" + "╯" + "─" + "╰" + "│" + ]; + }; + plugins.cmp-buffer.enable = true; + plugins.cmp-path.enable = true; + plugins.cmp-spell.enable = true; + plugins.cmp-emoji.enable = true; + plugins.cmp-nvim-lsp.enable = true; + }; +} diff --git a/home/programs/coding/nixvim/default.nix b/home/programs/coding/nixvim/default.nix new file mode 100644 index 0000000..37ebe3e --- /dev/null +++ b/home/programs/coding/nixvim/default.nix @@ -0,0 +1,136 @@ +{ + config, + pkgs, + ... +}: +{ + imports = [ + ./lsp.nix + ./cmp.nix + ./ai.nix + ./lualine.nix + ./treesitter.nix + ./hop.nix + ./ui.nix + ./mini.nix + ]; + stylix.targets.nixvim.enable = true; + programs.nixvim = { + enable = true; + defaultEditor = true; + performance.combinePlugins = { + enable = true; + standalonePlugins = [ + "nvim-treesitter" + "copilot.lua" + ]; + }; + globals = { + mapleader = " "; + maplocalleader = " "; + have_nerd_font = true; + }; + highlightOverride = { + CursorLineNr = { + bg = "#${config.lib.stylix.colors.base01}"; + fg = "#${config.lib.stylix.colors.base06}"; + }; + # LineNrAbove = { + # bg = "#${config.lib.stylix.colors.base00}"; + # fg = "#${config.lib.stylix.colors.base06}"; + # }; + # LineNrBelow = { + # bg = "#${config.lib.stylix.colors.base00}"; + # fg = "#${config.lib.stylix.colors.base07}"; + # }; + }; + opts = { + number = true; + relativenumber = true; + mouse = "a"; + showmode = false; + clipboard.providers.wl-copy.enable = true; + breakindent = true; + tabstop = 2; + shiftwidth = 2; + undofile = true; + ignorecase = true; + smartcase = true; + updatetime = 250; + timeoutlen = 300; + splitright = true; + splitbelow = true; + list = true; + listchars.__raw = "{ tab = '» ', trail = '·', nbsp = '␣' }"; + inccommand = "split"; + cursorline = true; + hlsearch = true; + scrolloff = 10; + spell = true; + spelllang = [ + "en_us" + "cjk" + ]; + spellsuggest = "best,4"; + }; + keymaps = [ + { + mode = "n"; + key = "S"; + action = ":w"; + } + { + mode = "n"; + key = "Q"; + action = ":bd"; + } + { + mode = "n"; + key = "1"; + action = ":BufferLineGoToBuffer 1"; + } + { + mode = "n"; + key = "2"; + action = ":BufferLineGoToBuffer 2"; + } + { + mode = "n"; + key = "2"; + action = ":BufferLineGoToBuffer 2"; + } + { + mode = "n"; + key = "3"; + action = ":BufferLineGoToBuffer 3"; + } + { + mode = "n"; + key = "4"; + action = ":BufferLineGoToBuffer 4"; + } + { + mode = "t"; + key = ""; + action = ""; + } + { + mode = "n"; + key = "o"; + action = ":lua MiniFiles.open()"; + } + ]; + plugins = { + sleuth.enable = true; # automatically set shiftwidth and expandtab based on the file + nvim-surround.enable = true; + repeat.enable = true; + lastplace.enable = true; + nvim-autopairs.enable = true; + endwise.enable = true; + markdown-preview.enable = true; + }; + extraPlugins = with pkgs.vimPlugins; [ + fcitx-vim + ]; + }; +} diff --git a/home/programs/coding/nixvim/hop.nix b/home/programs/coding/nixvim/hop.nix new file mode 100644 index 0000000..e258edb --- /dev/null +++ b/home/programs/coding/nixvim/hop.nix @@ -0,0 +1,15 @@ +{ + programs.nixvim.plugins.hop.enable = true; + programs.nixvim.keymaps = [ + { + mode = "n"; + key = "hw"; + action = ":HopWord"; + } + { + mode = "n"; + key = "hl"; + action = ":HopLine"; + } + ]; +} diff --git a/home/programs/coding/nixvim/lsp.nix b/home/programs/coding/nixvim/lsp.nix new file mode 100644 index 0000000..6b90da4 --- /dev/null +++ b/home/programs/coding/nixvim/lsp.nix @@ -0,0 +1,153 @@ +{ + pkgs, + lib, + inputs, + host, + user, + ... +}: +{ + programs.nixvim = { + plugins = { + lsp = { + enable = true; + inlayHints = true; + keymaps = { + diagnostic = { + "lE" = "open_float"; + "[" = "goto_prev"; + "]" = "goto_next"; + }; + lspBuf = { + "gD" = "declaration"; + "gd" = "definition"; + "gr" = "references"; + "gI" = "implementation"; + "gy" = "type_definition"; + }; + }; + servers = { + pyright.enable = true; + lua_ls.enable = true; + bashls.enable = true; + nixd = { + enable = true; + package = inputs.nixd.packages.${pkgs.system}.nixd; + settings = { + formatting.command = [ "nixfmt" ]; + nixd.nixpkgs.expr = "import { }"; + options = + let + flake = # nix + ''(builtins.getFlake "/home/${user}/.config/nixos")''; + in + { + nixos.expr = # nix + ''${flake}.nixosConfigurations.${host}.options''; + home_manager.expr = # nix + ''${flake}.homeConfigurations."${user}@${host}".options''; + }; + }; + }; + nil_ls = { + enable = true; + package = inputs.nil.packages.${pkgs.system}.nil; + settings = { + # formatting.command = ["nixfmt"]; + nix.flake = { + autoArchive = true; + }; + }; + }; + texlab.enable = true; + }; + }; + conform-nvim = { + enable = true; + settings = { + format_on_save = { + lsp_fallback = "fallback"; + timeout_ms = 500; + }; + notify_on_error = true; + + formatters_by_ft = { + bash = [ + "shfmt" + "shellcheck" + "shellharden" + ]; + css = [ "prettier" ]; + scss = [ "prettier" ]; + html = [ "prettier" ]; + json = [ "prettier" ]; + jsonc = [ "prettier" ]; + lua = [ "stylua" ]; + markdown = [ + "prettier" + "injected" + ]; + nix = [ + "nixfmt" + "injected" + ]; + yaml = [ "yamlfmt" ]; + python = [ "black" ]; + tex = [ "latexindent" ]; + }; + formatters = { + injeced.lang_to_ext = { + lua = "lua"; + }; + shfmt.command = lib.getExe pkgs.shfmt; + shellcheck.command = lib.getExe pkgs.shellcheck; + shellharden.command = lib.getExe pkgs.shellharden; + stylua.command = lib.getExe pkgs.stylua; + latexindent.prepend_args = [ ''-y="defaultIndent=' '"'' ]; + }; + }; + }; + lsp-format = { + enable = true; + }; + lspsaga = { + enable = true; + lightbulb.virtualText = true; + lightbulb.sign = false; + }; + trouble.enable = true; + lsp-signature.enable = true; + otter.enable = true; + }; + }; + programs.nixvim.keymaps = [ + { + mode = "n"; + key = "K"; + action = ":Lspsaga hover_doc"; + } + { + mode = "n"; + key = "lo"; + action = ":Lspsaga outline"; + } + { + mode = "n"; + key = "lr"; + action = ":Lspsaga rename"; + } + { + mode = "n"; + key = "la"; + action = ":Lspsaga code_action"; + } + { + mode = "n"; + key = "lf"; + action = ":Lspsaga finder"; + } + ]; + programs.nixvim.extraPlugins = with pkgs.vimPlugins; [ + nvim-docs-view + ]; +} diff --git a/home/programs/coding/nixvim/lualine.nix b/home/programs/coding/nixvim/lualine.nix new file mode 100644 index 0000000..cda4303 --- /dev/null +++ b/home/programs/coding/nixvim/lualine.nix @@ -0,0 +1,128 @@ +{ config, ... }: +let + colors = config.lib.stylix.colors; +in +{ + programs.nixvim.plugins.lualine = { + enable = true; + settings = { + options = { + theme = { + normal = { + a = { + bg = "#nil"; + }; + b = { + bg = "nil"; + }; + c = { + bg = "nil"; + }; + z = { + bg = "nil"; + }; + y = { + bg = "nil"; + }; + }; + }; + globalstatus = true; + disabled_filetypes = { + statusline = [ + "dashboard" + "alpha" + "starter" + "snacks_dashboard" + ]; + }; + }; + inactive_sections = { + lualine_x = [ + "filename" + "filetype" + ]; + }; + sections = { + lualine_a = [ + { + __unkeyed = "mode"; + fmt = "string.lower"; + color = { + fg = colors.base0E; + bg = "nil"; + }; + separator.left = ""; + separator.right = ""; + } + ]; + lualine_b = [ + { + __unkeyed = "branch"; + icon.__unkeyed = ""; + color = { + bg = "nil"; + }; + separator.left = ""; + separator.right = ""; + } + { + __unkeyed = "diff"; + separator.left = ""; + separator.right = ""; + } + ]; + lualine_c = [ + { + __unkeyed = "diagnostic"; + symbols = { + error = " "; + warn = " "; + info = " "; + hint = "󰝶 "; + }; + color = { + fg = colors.base08; + bg = "nil"; + }; + separator.left = ""; + separator.right = ""; + } + ]; + lualine_x = [ "" ]; + lualine_y = [ + # { + # __unkeyed = "filetype"; + # icon_only = true; + # separator.left = ""; + # separator.right = ""; + # } + { + __unkeyed = "filename"; + # symbols = { + # modified = ""; + # readonly = "👁️"; + # unnamed = ""; + # }; + color = { + fg = colors.base05; + bg = "nil"; + }; + separator.left = ""; + separator.right = ""; + } + ]; + lualine_z = [ + { + __unkeyed = "location"; + color = { + fg = colors.base0B; + bg = "nil"; + }; + separator.left = ""; + separator.right = ""; + } + ]; + }; + }; + }; +} diff --git a/home/programs/coding/nixvim/mini.nix b/home/programs/coding/nixvim/mini.nix new file mode 100644 index 0000000..1b24219 --- /dev/null +++ b/home/programs/coding/nixvim/mini.nix @@ -0,0 +1,29 @@ +{ + programs.nixvim.plugins = { + mini.enable = true; + mini.modules = { + align = { }; + files = { + mappings = { + close = ""; + }; + }; + hipatterns = { + hex_color = "hipatterns.gen_highlighter.hex_color()"; + }; + indentscope = { }; + move = { + mappings = { + left = ""; + right = ""; + up = ""; + down = ""; + line_left = ""; + line_right = ""; + line_up = ""; + line_down = ""; + }; + }; + }; + }; +} diff --git a/home/programs/coding/nixvim/treesitter.nix b/home/programs/coding/nixvim/treesitter.nix new file mode 100644 index 0000000..5ba7f6b --- /dev/null +++ b/home/programs/coding/nixvim/treesitter.nix @@ -0,0 +1,30 @@ +{ pkgs, ... }: +{ + programs.nixvim.plugins.treesitter = { + enable = true; + settings.auto_install = true; + settings.ensure_installed = [ + "diff" + "bash" + "fish" + "python" + "yaml" + "lua" + "json" + "nix" + "regex" + "toml" + "vim" + "markdown" + "rust" + "jsonc" + "glsl" + "css" + "hyprlang" + ]; + settings.highlight.enable = true; + settings.indent.enable = true; + }; + programs.nixvim.plugins.hmts.enable = true; + programs.nixvim.plugins.rainbow-delimiters.enable = true; +} diff --git a/home/programs/coding/nixvim/ui.nix b/home/programs/coding/nixvim/ui.nix new file mode 100644 index 0000000..0e54c89 --- /dev/null +++ b/home/programs/coding/nixvim/ui.nix @@ -0,0 +1,93 @@ +{ pkgs, ... }: +{ + programs.nixvim.plugins = { + web-devicons.enable = true; + telescope.enable = true; + bufferline.enable = true; + scrollview.enable = true; + cursorline.enable = true; + cursorline.settings.cursorword.enable = true; + fidget.enable = true; + indent-blankline.enable = true; + gitsigns.enable = true; + navbuddy.enable = true; + smartcolumn.enable = true; + fastaction.enable = true; + colorizer = { + enable = true; + settings.user_default_options = { + mode = "virtualtext"; + css = true; + css_fn = true; + names = false; + virtualtext = "■"; + virtualtext_inline = true; + virtualtext_mode = "foreground"; + }; + }; + which-key = { + enable = true; + settings.spec = [ + { + __unkeyed = "f"; + name = "Telescope"; + } + { + __unkeyed = "l"; + name = "LSP"; + } + { + __unkeyed = "m"; + name = "Minimap"; + } + { + __unkeyed = "c"; + name = "CodeCompanion"; + } + { + __unkeyed = "h"; + name = "Hop"; + } + ]; + }; + render-markdown = { + enable = true; + settings.file_types = [ + "markdown" + "codecompanion" + ]; + }; + }; + programs.nixvim.keymaps = [ + { + mode = "n"; + key = "ff"; + action = ":Telescope fd"; + } + { + mode = "n"; + key = "fd"; + action = ":Telescope diagnostics"; + } + { + mode = "n"; + key = "fb"; + action = ":Telescope buffers"; + } + { + mode = "n"; + key = "fr"; + action = ":Telescope registers"; + } + ]; + programs.nixvim.extraPlugins = with pkgs.vimPlugins; [ + codewindow-nvim + ]; + programs.nixvim.extraConfigLua = + # lua + '' + local codewindow = require("codewindow") + codewindow.setup() + codewindow.apply_default_keybinds() + ''; +} diff --git a/home/programs/coding/python.nix b/home/programs/coding/python.nix new file mode 100644 index 0000000..37aaacf --- /dev/null +++ b/home/programs/coding/python.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + python312 + python312Packages.pip + python312Packages.virtualenv + python312Packages.numpy + python312Packages.matplotlib + python312Packages.pandas + black + conda + ]; +} diff --git a/home/programs/coding/tex.nix b/home/programs/coding/tex.nix new file mode 100644 index 0000000..b206489 --- /dev/null +++ b/home/programs/coding/tex.nix @@ -0,0 +1,6 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + texlive.combined.scheme-full + ]; +} diff --git a/home/programs/coding/zed.nix b/home/programs/coding/zed.nix new file mode 100644 index 0000000..7356c50 --- /dev/null +++ b/home/programs/coding/zed.nix @@ -0,0 +1,57 @@ +{ + programs.zed-editor = { + enable = true; + extensions = [ + "latex" + "python" + ]; + userSettings = { + features = { + copilot = true; + }; + vim_mode = true; + hover_popover_enabled = true; + confirm_quit = true; + show_completions_on_input = true; + show_completion_documentation = true; + auto_signature_help = true; + show_inline_completions_in_menu = true; + show_wrap_guides = true; + show_inline_completions = true; + show_whitespaces = "all"; + vertical_scroll_margin = 5; + relative_line_numbers = true; + inlay_hints.enabled = true; + assistant = { + enabled = true; + # default_model = { + # provider = "deepseek"; + # model = "deepseek-reasoner"; + # }; + }; + enable_language_server = true; + format_on_save = "on"; + telemetry = { + diagnostics = false; + metrics = false; + }; + auto_update = false; + auto_install_extensions.html = false; + languages = { + Markdown.format_on_save = true; + }; + # language_models.deepseek = { + # api_url = "https://api.deepseek.com"; + # available_models = [ + # { + # name = "deepseek-reasoner"; + # display_name = "DeepSeek Reasoner"; + # max_tokens = 64000; + # max_output_tokens = 4096; + # } + # ]; + # }; + }; + }; + stylix.targets.zed.enable = true; +} diff --git a/home/programs/default.nix b/home/programs/default.nix new file mode 100644 index 0000000..a5ea077 --- /dev/null +++ b/home/programs/default.nix @@ -0,0 +1,14 @@ +{ + imports = [ + ./desktop + ./shell + ./coding + ./terminal + ./utils + ./browser + ./documents + ./network + ./study + ./social + ]; +} diff --git a/home/programs/desktop/default.nix b/home/programs/desktop/default.nix new file mode 100644 index 0000000..1990547 --- /dev/null +++ b/home/programs/desktop/default.nix @@ -0,0 +1,19 @@ +{ pkgs, ... }: +{ + imports = [ + ./tofi.nix + ./fonts.nx + ./mako.nix + ./niri + ]; + home.packages = with pkgs; [ + swww + kanshi + wlsunset + wayneko + ]; + home.file."scripts" = { + source = ./scripts; + recursive = true; + }; +} diff --git a/home/programs/desktop/fonts.nix b/home/programs/desktop/fonts.nix new file mode 100644 index 0000000..48aa14b --- /dev/null +++ b/home/programs/desktop/fonts.nix @@ -0,0 +1,17 @@ +{ pkgs, lib, ... }: +{ + xdg.dataFile."fonts" = { + source = ./fonts; + recursive = true; + }; + home.packages = [ + (import ../../../../pkgs/fonts/kose.nix { + inherit pkgs lib; + inherit (pkgs) stdenvNoCC; + }) + (import ../../../../pkgs/fonts/hugmetight.nix { + inherit pkgs lib; + inherit (pkgs) stdenvNoCC; + }) + ]; +} diff --git a/home/programs/desktop/mako.nix b/home/programs/desktop/mako.nix new file mode 100644 index 0000000..5475e9d --- /dev/null +++ b/home/programs/desktop/mako.nix @@ -0,0 +1,10 @@ +{ pkgs, ... }: +{ + services.mako = { + enable = true; + borderSize = 4; + borderRadius = 8; + }; + stylix.targets.mako.enable = true; + home.packages = [ pkgs.libnotify ]; +} diff --git a/home/programs/desktop/niri/animations.nix b/home/programs/desktop/niri/animations.nix new file mode 100644 index 0000000..cb492b0 --- /dev/null +++ b/home/programs/desktop/niri/animations.nix @@ -0,0 +1,231 @@ +{ + programs.niri.settings.animations = { + window-open = { + spring = { + damping-ratio = 0.7; + stiffness = 300; + epsilon = 0.001; + }; + }; + window-close = { + easing = { + curve = "ease-out-quad"; + duration-ms = 500; + }; + }; + workspace-switch = { + spring = { + damping-ratio = 0.8; + stiffness = 600; + epsilon = 0.001; + }; + }; + horizontal-view-movement = { + spring = { + damping-ratio = 1.0; + stiffness = 600; + epsilon = 0.001; + }; + }; + window-movement = { + spring = { + damping-ratio = 1.0; + stiffness = 600; + epsilon = 0.001; + }; + }; + shaders = { + window-open = + # glsl + '' + // Example: fill the current geometry with a solid vertical gradient and + // gradually make opaque. + vec4 solid_gradient(vec3 coords_geo, vec3 size_geo) { + vec4 color = vec4(0.0); + + // Paint only the area inside the current geometry. + if (0.0 <= coords_geo.x && coords_geo.x <= 1.0 + && 0.0 <= coords_geo.y && coords_geo.y <= 1.0) + { + vec4 from = vec4(1.0, 0.0, 0.0, 1.0); + vec4 to = vec4(0.0, 1.0, 0.0, 1.0); + color = mix(from, to, coords_geo.y); + } + + // Make it opaque. + color *= niri_clamped_progress; + + return color; + } + + // Example: gradually scale up and make opaque, equivalent to the default + // opening animation. + vec4 default_open(vec3 coords_geo, vec3 size_geo) { + // Scale up the window. + float scale = max(0.0, (niri_progress / 2.0 + 0.5)); + coords_geo = vec3((coords_geo.xy - vec2(0.5)) / scale + vec2(0.5), 1.0); + + // Get color from the window texture. + vec3 coords_tex = niri_geo_to_tex * coords_geo; + vec4 color = texture2D(niri_tex, coords_tex.st); + + // Make the window opaque. + color *= niri_clamped_progress; + + return color; + } + + // Example: show the window as an expanding circle. + // Recommended setting: duration-ms 250 + vec4 expanding_circle(vec3 coords_geo, vec3 size_geo) { + vec3 coords_tex = niri_geo_to_tex * coords_geo; + vec4 color = texture2D(niri_tex, coords_tex.st); + + vec2 coords = (coords_geo.xy - vec2(0.5, 0.5)) * size_geo.xy * 2.0; + coords = coords / length(size_geo.xy); + float p = niri_clamped_progress; + if (p * p <= dot(coords, coords)) + color = vec4(0.0); + + return color * p; + } + + // This is the function that you must define. + vec4 open_color(vec3 coords_geo, vec3 size_geo) { + // You can pick one of the example functions or write your own. + return default_open(coords_geo, size_geo); + } + ''; + window-resize = + # glsl + '' + // Example: fill the current geometry with a solid vertical gradient. + vec4 solid_gradient(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords = coords_curr_geo; + vec4 color = vec4(0.0); + + // Paint only the area inside the current geometry. + if (0.0 <= coords.x && coords.x <= 1.0 + && 0.0 <= coords.y && coords.y <= 1.0) + { + vec4 from = vec4(1.0, 0.0, 0.0, 1.0); + vec4 to = vec4(0.0, 1.0, 0.0, 1.0); + color = mix(from, to, coords.y); + } + + return color; + } + + // Example: crossfade between previous and next texture, stretched to the + // current geometry. + vec4 crossfade(vec3 coords_curr_geo, vec3 size_curr_geo) { + // Convert coordinates into the texture space for sampling. + vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo; + vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st); + + // Convert coordinates into the texture space for sampling. + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st); + + vec4 color = mix(color_prev, color_next, niri_clamped_progress); + return color; + } + + // Example: next texture, stretched to the current geometry. + vec4 stretch_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo; + vec4 color = texture2D(niri_tex_next, coords_tex_next.st); + return color; + } + + // Example: next texture, stretched to the current geometry if smaller, and + // cropped if bigger. + vec4 stretch_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo; + + vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo; + vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo; + + // We can crop if the current window size is smaller than the next window + // size. One way to tell is by comparing to 1.0 the X and Y scaling + // coefficients in the current-to-next transformation matrix. + bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0; + bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0; + + vec3 coords = coords_stretch; + if (can_crop_by_x) + coords.x = coords_crop.x; + if (can_crop_by_y) + coords.y = coords_crop.y; + + vec4 color = texture2D(niri_tex_next, coords.st); + + // However, when we crop, we also want to crop out anything outside the + // current geometry. This is because the area of the shader is unspecified + // and usually bigger than the current geometry, so if we don't fill pixels + // outside with transparency, the texture will leak out. + // + // When stretching, this is not an issue because the area outside will + // correspond to client-side decoration shadows, which are already supposed + // to be outside. + if (can_crop_by_x && (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x)) + color = vec4(0.0); + if (can_crop_by_y && (coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y)) + color = vec4(0.0); + + return color; + } + + // Example: cropped next texture if it's bigger than the current geometry, and + // crossfade between previous and next texture otherwise. + vec4 crossfade_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo; + vec3 coords_prev_geo = niri_curr_geo_to_prev_geo * coords_curr_geo; + + vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo; + vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo; + vec3 coords_stretch_prev = niri_geo_to_tex_prev * coords_curr_geo; + + // We can crop if the current window size is smaller than the next window + // size. One way to tell is by comparing to 1.0 the X and Y scaling + // coefficients in the current-to-next transformation matrix. + bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0; + bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0; + bool crop = can_crop_by_x && can_crop_by_y; + + vec4 color; + + if (crop) { + // However, when we crop, we also want to crop out anything outside the + // current geometry. This is because the area of the shader is unspecified + // and usually bigger than the current geometry, so if we don't fill pixels + // outside with transparency, the texture will leak out. + // + // When crossfading, this is not an issue because the area outside will + // correspond to client-side decoration shadows, which are already supposed + // to be outside. + if (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x || + coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y) { + color = vec4(0.0); + } else { + color = texture2D(niri_tex_next, coords_crop.st); + } + } else { + // If we can't crop, then crossfade. + color = texture2D(niri_tex_next, coords_stretch.st); + vec4 color_prev = texture2D(niri_tex_prev, coords_stretch_prev.st); + color = mix(color_prev, color, niri_clamped_progress); + } + + return color; + } + + // This is the function that you must define. + vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + // You can pick one of the example functions or write your own. + return crossfade_or_crop_next(coords_curr_geo, size_curr_geo); + } + ''; + }; + }; +} diff --git a/home/programs/desktop/niri/autostart.nix b/home/programs/desktop/niri/autostart.nix new file mode 100644 index 0000000..6155940 --- /dev/null +++ b/home/programs/desktop/niri/autostart.nix @@ -0,0 +1,158 @@ +{ + pkgs, + lib, + user, + ... +}: +let + niri-autostart = pkgs.writeShellApplication { + name = "niri-autostart"; + runtimeInputs = with pkgs; [ + niri-blur-wallpaper + swww + clash-meta + wlsunset + ]; + extraShellCheckFlags = [ ]; + bashOptions = [ ]; + text = + # bash + '' + swww kill + swww-daemon & + clash-meta -d ~/.config/clash & + wlsunset -s 00:00 -S 00:00 -t 5000 -T 5001 & + niri-blur-wallpaper & + ''; + }; + niri-blur-wallpaper = pkgs.writers.writePython3Bin "niri-blur-wallpaper" { doCheck = false; } '' + import os + import subprocess + import json + + wallpapers_path = "/home/${user}/Pictures/Wallpapers/generated/" + wallpapers_cache_path = "/home/${user}/.cache/swww/" + events_of_interest = [ + "Workspace changed", + "Workspace focused", + "Window opened", + "Window closed", + ] + + + def get_niri_msg_output(msg): + output = subprocess.check_output(["niri", "msg", "-j", msg]) + output = json.loads(output) + return output + + + def get_current_wallpaper(monitor): + with open(os.path.join(wallpapers_cache_path, monitor)) as f: + wallpaper = str(f.read()) + return wallpaper + + + def set_wallpaper(monitor, wallpaper): + subprocess.run( + [ + "swww", + "img", + "--transition-type", + "fade", + "--transition-duration", + "0.5", + "-o", + monitor, + wallpaper, + ] + ) + + + def get_wallpaper_name(wallpaper_path): + current_wallpaper_is_blurred = "blurred" in wallpaper_path + if current_wallpaper_is_blurred: + wallpaper_name = "-".join(wallpaper_path.split("-")[1:-1]) + else: + wallpaper_name = "-".join(wallpaper_path.split("-")[1:]) + wallpaper_name = ".".join(wallpaper_name.split(".")[:-1]) + return wallpaper_name + + + def set_wallpaper_if_needed(active_workspace, init): + active_workspace_monitor = active_workspace["output"] + current_wallpaper = get_current_wallpaper(active_workspace_monitor) + current_wallpaper_is_live = current_wallpaper.endswith(".gif") + if current_wallpaper_is_live: + return + wallpaper_name = get_wallpaper_name(current_wallpaper) + active_workspace_is_empty = active_workspace["active_window_id"] is None + if active_workspace_is_empty: + wallpaper = os.path.join(wallpapers_path, f"{wallpaper_name}.jpg") + else: + wallpaper = os.path.join(wallpapers_path, f"{wallpaper_name}-blurred.jpg") + real_wallpaper = os.path.realpath(wallpaper) + if current_wallpaper == real_wallpaper and not init: + return + set_wallpaper(active_workspace_monitor, wallpaper) + + + def change_wallpaper(init=False): + workspaces = get_niri_msg_output("workspaces") + active_workspaces = [ + workspace for workspace in workspaces if workspace["is_active"] + ] + for active_workspace in active_workspaces: + set_wallpaper_if_needed(active_workspace, init) + + + def main(): + change_wallpaper(init=True) + event_stream = subprocess.Popen( + ["niri", "msg", "event-stream"], stdout=subprocess.PIPE + ) + if not event_stream.stdout: + return + for line in iter(event_stream.stdout.readline, ""): + if any(event in line.decode() for event in events_of_interest): + change_wallpaper() + + + if __name__ == "__main__": + main() + ''; +in +{ + systemd.user.services.niri-blur-wallpaper = { + Unit = { + Description = "Niri Blur Wallpaper"; + BindsTo = [ "graphical-session.target" ]; + Before = [ "graphical-session.target" ]; + Wants = [ "graphical-session-pre.target" ]; + After = [ "graphical-session-pre.target" ]; + }; + Install = { + WantedBy = [ "graphical-session.target" ]; + Wants = [ "xdg-desktop-autostart.target" ]; + After = [ "xdg-desktop-autostart.target" ]; + }; + Service = { + Type = "simple"; + ExecStart = "${niri-blur-wallpaper}/bin/niri-blur-wallpaper"; + Restart = "on-failure"; + RestartSec = "5s"; + }; + }; + home.activation.restart-niri-blur-wallpaper = + lib.hm.dag.entryAfter [ "reload-waybar" ] + # bash + ''run --quiet ${pkgs.systemd}/bin/systemctl --user restart niri-blur-wallpaper''; + programs.niri.settings.spawn-at-startup = [ + { command = [ "${niri-autostart}/bin/niri-autostart" ]; } + { + command = [ + "fcitx5" + "-d" + ]; + } + ]; +} diff --git a/home/programs/desktop/niri/binds.nix b/home/programs/desktop/niri/binds.nix new file mode 100644 index 0000000..316a64b --- /dev/null +++ b/home/programs/desktop/niri/binds.nix @@ -0,0 +1,117 @@ +{ config, user, ... }: +{ + programs.niri.settings.binds = with config.lib.niri.actions; { + "Mod+Return".action = spawn "kitty"; + "Mod+X".action = spawn "/home/${user}/scripts/tofi/powermenu"; + "Mod+Shift+C".action = spawn "/home/${user}/scripts/tofi/colorscheme"; + "Mod+P".action = spawn "sh" "-c" "$(tofi-run)"; + "Mod+Shift+P".action = spawn "tofi-drun" "--drun-launch=true"; + "Mod+Shift+W".action = spawn "/home/${user}/scripts/change-wal-niri"; + "Mod+B".action = spawn "pkill" "-USR1" ".waybar-wrapped"; + "Mod+Q".action = close-window; + "Mod+E".action = expand-column-to-available-width; + "Mod+ALt+C".action = spawn "wl-color-picker"; + "Mod+N".action = + spawn "wayneko" "--layer" "top" "--follow-pointer" "true" "--survive-close" "--background-colour" + "0x${config.lib.stylix.colors.base0E}dd" + "--outline-colour" + "0x${config.lib.stylix.colors.base01}dd"; + + "Mod+Shift+Slash".action = show-hotkey-overlay; + "Mod+T".action = toggle-column-tabbed-display; + + "XF86AudioMute".action = spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; + "XF86AudioMicMute".action = spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; + "XF86AudioRaiseVolume".action = spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; + "XF86AudioLowerVolume".action = spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; + + "Mod+Left".action = focus-column-left; + "Mod+Down".action = focus-window-down; + "Mod+Up".action = focus-window-up; + "Mod+Right".action = focus-column-right; + "Mod+H".action = focus-column-or-monitor-left; + "Mod+J".action = focus-window-or-workspace-down; + "Mod+K".action = focus-window-or-workspace-up; + "Mod+L".action = focus-column-or-monitor-right; + + "Mod+Shift+Left".action = move-column-left; + "Mod+Shift+Down".action = move-window-down; + "Mod+Shift+Up".action = move-window-up; + "Mod+Shift+Right".action = move-column-right; + "Mod+Shift+H".action = move-column-left-or-to-monitor-left; + "Mod+Shift+J".action = move-window-down-or-to-workspace-down; + "Mod+Shift+K".action = move-window-up-or-to-workspace-up; + "Mod+Shift+L".action = move-column-right-or-to-monitor-right; + + "Mod+Ctrl+Left".action = focus-monitor-left; + "Mod+Ctrl+Down".action = focus-monitor-down; + "Mod+Ctrl+Up".action = focus-monitor-up; + "Mod+Ctrl+Right".action = focus-monitor-right; + "Mod+Ctrl+H".action = focus-monitor-left; + "Mod+Ctrl+J".action = focus-monitor-down; + "Mod+Ctrl+K".action = focus-monitor-up; + "Mod+Ctrl+L".action = focus-monitor-right; + + "Mod+Shift+Ctrl+Left".action = move-window-to-monitor-left; + "Mod+Shift+Ctrl+Down".action = move-window-to-monitor-down; + "Mod+Shift+Ctrl+Up".action = move-window-to-monitor-up; + "Mod+Shift+Ctrl+Right".action = move-window-to-monitor-right; + "Mod+Shift+Ctrl+H".action = move-window-to-monitor-left; + "Mod+Shift+Ctrl+J".action = move-window-to-monitor-down; + "Mod+Shift+Ctrl+K".action = move-window-to-monitor-up; + "Mod+Shift+Ctrl+L".action = move-window-to-monitor-right; + + "Mod+Shift+Space".action = toggle-window-floating; + "Mod+Space".action = switch-focus-between-floating-and-tiling; + + "Mod+TouchpadScrollDown" = { + action = focus-workspace-down; + cooldown-ms = 150; + }; + "Mod+TouchpadScrollUp" = { + action = focus-workspace-up; + cooldown-ms = 150; + }; + + "Mod+1".action = focus-workspace 1; + "Mod+2".action = focus-workspace 2; + "Mod+3".action = focus-workspace 3; + "Mod+4".action = focus-workspace 4; + "Mod+5".action = focus-workspace 5; + "Mod+6".action = focus-workspace 6; + "Mod+7".action = focus-workspace 7; + "Mod+8".action = focus-workspace 8; + "Mod+9".action = focus-workspace 9; + "Mod+Ctrl+1".action = move-window-to-workspace 1; + "Mod+Ctrl+2".action = move-window-to-workspace 2; + "Mod+Ctrl+3".action = move-window-to-workspace 3; + "Mod+Ctrl+4".action = move-window-to-workspace 4; + "Mod+Ctrl+5".action = move-window-to-workspace 5; + "Mod+Ctrl+6".action = move-window-to-workspace 6; + "Mod+Ctrl+7".action = move-window-to-workspace 7; + "Mod+Ctrl+8".action = move-window-to-workspace 8; + "Mod+Ctrl+9".action = move-window-to-workspace 9; + + "Mod+Comma".action = consume-window-into-column; + "Mod+Period".action = expel-window-from-column; + "Mod+BracketLeft".action = consume-or-expel-window-left; + "Mod+BracketRight".action = consume-or-expel-window-left; + + "Mod+R".action = switch-preset-column-width; + "Mod+Shift+R".action = reset-window-height; + "Mod+F".action = maximize-column; + "Mod+Shift+F".action = fullscreen-window; + "Mod+C".action = center-column; + + "Mod+Minus".action = set-column-width "-10%"; + "Mod+Equal".action = set-column-width "+10%"; + "Mod+Shift+Minus".action = set-window-height "-10%"; + "Mod+Shift+Equal".action = set-window-height "+10%"; + + "Print".action = screenshot; + "Ctrl+Print".action = screenshot-screen; + "Alt+Print".action = screenshot-window; + + "Mod+Shift+E".action = quit; + }; +} diff --git a/home/programs/desktop/niri/default.nix b/home/programs/desktop/niri/default.nix new file mode 100644 index 0000000..ee522ba --- /dev/null +++ b/home/programs/desktop/niri/default.nix @@ -0,0 +1,169 @@ +{ + config, + pkgs, + ... +}: +{ + imports = [ + ./binds.nix + ./animations.nix + ./waybar.nix + ./autostart.nix + ./override-config.nix + ]; + + programs.niri = { + # home.file.".config/niri/config.kdl".source = ./config.kdl; + enable = true; + package = pkgs.niri-unstable; + settings = + with config.lib.stylix.colors.withHashtag; + let + shadowConfig = { + enable = true; + spread = 0; + softness = 10; + color = "#000000dd"; + }; + in + { + hotkey-overlay.skip-at-startup = true; + prefer-no-csd = true; + input = { + focus-follows-mouse.enable = true; + touchpad.natural-scroll = false; + keyboard.xkb.options = "caps:escape"; + }; + outputs = builtins.mapAttrs (name: value: { + inherit (value) scale mode position; + transform.rotation = value.rotation; + background-color = base01; + }) config.monitors; + window-rules = [ + { + geometry-corner-radius = { + bottom-left = 10.0; + bottom-right = 10.0; + top-left = 10.0; + top-right = 10.0; + }; + clip-to-geometry = true; + draw-border-with-background = false; + } + { + matches = [ + { app-id = "yad"; } + ]; + open-floating = true; + } + { + matches = [ + { app-id = "firefox"; } + { app-id = "org.qutebrowser.qutebrowser"; } + { app-id = "kitty"; } + { app-id = "evince"; } + { app-id = "zathura"; } + ]; + default-column-width = { + proportion = 1.0; + }; + } + { + matches = [ + { is-focused = true; } + ]; + opacity = 0.95; + } + { + matches = [ + { is-focused = false; } + ]; + opacity = 0.85; + } + ]; + layer-rules = [ + { + matches = [ { namespace = "launcher"; } ]; + geometry-corner-radius = { + bottom-left = 15.0; + bottom-right = 15.0; + top-left = 15.0; + top-right = 15.0; + }; + shadow = shadowConfig; + } + ]; + gestures = { + dnd-edge-view-scroll = { + trigger-width = 60; + delay-ms = 100; + max-speed = 1500; + }; + }; + workspaces = { + "1" = { + open-on-output = config.mainMonitorName; + name = "coding"; + }; + "2" = { + open-on-output = config.mainMonitorName; + name = "browsing"; + }; + "3" = { + open-on-output = builtins.head config.otherMonitorsNames; + name = "reading"; + }; + "4" = { + open-on-output = config.mainMonitorName; + name = "music"; + }; + }; + layout = { + gaps = 12; + border = { + enable = true; + width = 4; + active = { + gradient = { + from = base07; + to = base0E; + angle = 45; + in' = "oklab"; + # relative-to = "workspace-view"; + }; + }; + # inactive.color = "#585b70"; + inactive.color = base02; + }; + focus-ring.enable = false; + struts = { + left = 2; + right = 2; + top = 0; + bottom = 2; + }; + insert-hint = { + enable = true; + display = { + gradient = { + # from = "#f9e2af"; + from = base0A; + # to = "#eba0ac"; + to = base09; + angle = 45; + }; + }; + }; + shadow = shadowConfig; + tab-indicator = { + hide-when-single-tab = true; + gap = 5; + width = 6; + length.total-proportion = 0.5; + position = "right"; + gaps-between-tabs = 2; + }; + }; + }; + }; +} diff --git a/home/programs/desktop/niri/override-config.nix b/home/programs/desktop/niri/override-config.nix new file mode 100644 index 0000000..5f5a67e --- /dev/null +++ b/home/programs/desktop/niri/override-config.nix @@ -0,0 +1,48 @@ +{ config, ... }: +let + # tabIndicatorConfig = # kdl + # '' + # tab-indicator { + # hide-when-single-tab + # gap 5 + # width 6 + # length total-proportion=0.5 + # position "right" + # gaps-between-tabs 2 + # } + # ''; + # shadowConfig = + # # kdl + # '' + # shadow { + # on + # spread 0 + # softness 10 + # color "#000000dd" + # } + # ''; + # tabBinds = # kdl + # '' + # Mod+T { toggle-column-tabbed-display; } + # ''; + extraConfig = + # kdl + ''''; + finalNiriConfig = + # builtins.replaceStrings + # [ "layout {" ] + # [ + # ("layout {\n" + shadowConfig + "\n" + tabIndicatorConfig) + # ] + # (config.programs.niri.finalConfig + "\n" + extraConfig) + # |> + # builtins.replaceStrings + # [ "binds {" ] + # [ + # ("binds {\n" + tabBinds) + # ]; + config.programs.niri.finalConfig + "\n" + extraConfig; +in +{ + home.file.".config/niri/config-override.kdl".text = finalNiriConfig; +} diff --git a/home/programs/desktop/niri/waybar.nix b/home/programs/desktop/niri/waybar.nix new file mode 100644 index 0000000..cfa1595 --- /dev/null +++ b/home/programs/desktop/niri/waybar.nix @@ -0,0 +1,420 @@ +{ config, ... }: +let + moduleConfiguration = + # jsonc + '' + // Modules configuration + "niri/workspaces": { + "format": "{icon}", + "format-icons": { + // "coding": " ", + // "browsing": " ", + // "reading": "󰈙", + // "default": "󱄅 ", + // "default": " ", + // "active": " " + "default": "" + }, + }, + "niri/window": { + "format": "{}", + "separate-outputs": true, + "icon": true, + "icon-size": 18 + }, + "memory": { + "interval": 30, + "format": " {used:0.1f}G/{total:0.1f}G", + "on-click": "kitty --class=htop,htop -e htop" + }, + "backlight": { + "device": "intel_backlight", + "on-scroll-up": "light -A 1", + "on-scroll-down": "light -U 1", + "format": "{icon} {percent}%", + "format-icons": [ + "", + "" + ] + }, + "tray": { + "icon-size": 16, + "spacing": 10 + }, + "clock": { + "format": " {:%a %d %H:%M}", + "tooltip-format": "{:%Y %B}\n{calendar}", + "on-click": "kitty --class=clock,clock --title=clock -o remember_window_size=no -o initial_window_width=600 -o initial_window_height=200 -e tty-clock -s -c -C 5" + }, + "battery": { + "states": { + "warning": 30, + "critical": 15, + }, + "format": "{icon} {capacity}%", + "format-warning": "{icon} {capacity}%", + "format-critical": "{icon} {capacity}%", + "format-charging": "{capacity}%", + "format-plugged": "{capacity}%", + "format-alt": "{icon} {time}", + "format-full": "{capacity}%", + "format-icons": [ + "", + "", + "", + "", + "" + ], + "tooltip-format": "{time}", + "interval": 5 + }, + "network": { + "format-wifi": "󰖩 {essid}", + "format-ethernet": "󰤭 Disconnected", + "format-linked": "{ifname} (No IP) 󱚵", + "format-disconnected": "Disconnected", + "tooltip-format-wifi": "Signal Strenght: {signalStrength}%", + "on-click": "kitty --class nmtui,nmtui --title=nmtui -o remember_window_size=no -o initial_window_width=400 -o initial_window_height=400 -e doas nmtui" + }, + "pulseaudio": { + "on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle", + "on-scroll-up": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.01+", + "on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.01-", + "format": "{icon} {volume}%", + "format-muted": "Muted", + "format-icons": { + "headphone": "󱡏", + "hands-free": "", + "headset": "󱡏", + "phone": "", + "portable": "", + "car": "", + "default": [ + "󰕿", + "󰖀", + "󰕾", + "󰕾" + ] + } + }, + "group/meters": { + "orientation": "inherit", + "drawer": { + "transition-duration": 500, + "transition-left-to-right": false, + }, + "modules": [ + "battery", + "memory", + "network", + "pulseaudio", + "backlight" + ] + } + ''; + trayBackgroundColor = if config.stylix.polarity == "dark" then "@base00" else "@base05"; + colors = { + inherit (config.lib.stylix.colors.withHashtag) + base00 + base01 + base04 + base05 + base06 + base07 + base08 + base0A + base0D + base0E + base0F + ; + }; +in +{ + imports = [ ../../../../lib/monitors.nix ]; + + programs.waybar = { + enable = true; + systemd = { + enable = true; + target = config.wayland.systemd.target; + }; + }; + home.file = { + ".config/waybar/config.jsonc".text = + let + otherMonitorsConfig = + map ( + name: + # json + '' + { + "position": "top", + "layer": "top", + "output": "${name}", + "modules-left": [ + "niri/workspaces", + "niri/window" + ], + "modules-right": [ + "tray", + "group/meters" + ], + ${moduleConfiguration} + }, + '') config.otherMonitorsNames + |> builtins.concatStringsSep "\n"; + in + # json + '' + [ + ${otherMonitorsConfig} + { + "position": "top", + "layer": "top", + "output": "${config.mainMonitorName}", + "modules-left": [ + "niri/workspaces", + "tray", + "niri/window" + ], + "modules-center": [ + "clock", + "memory" + ], + "modules-right": [ + "network", + "pulseaudio", + "backlight", + "battery", + ], + ${moduleConfiguration} + } + ] + + ''; + ".config/waybar/colors.css".text = + # css + (builtins.mapAttrs (name: value: "@define-color ${name} ${value};") colors) + |> builtins.attrValues + |> builtins.concatStringsSep "\n"; + ".config/waybar/tray.css".text = + # css + '' + #tray { + background: shade(alpha(${trayBackgroundColor}, 0.9), 1); + } + ''; + ".config/waybar/style.css".text = + # css + '' + @import "animation.css"; + @import "colors.css"; + @import "tray.css"; + + * { + /* all: unset; */ + font-size: 14px; + /* font-family: "Comic Mono Nerd Font"; */ + font-family: "Hug Me Tight", "Xiaolai SC"; + min-height: 0; + } + + window#waybar { + /* border-radius: 15px; */ + /* color: @base04; */ + background: transparent; + } + + tooltip { + background: @base01; + border-radius: 5px; + border-width: 2px; + border-style: solid; + border-color: @base07; + } + + #network, + #clock, + #battery, + #pulseaudio, + #workspaces, + #backlight, + #memory, + #tray, + #window { + padding: 4px 10px; + background: shade(alpha(@base00, 0.9), 1); + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.377); + color: @base05; + margin-top: 10px; + margin-bottom: 5px; + margin-left: 5px; + margin-right: 5px; + box-shadow: 1px 2px 2px #101010; + border-radius: 10px; + } + + #workspaces { + margin-left: 15px; + font-size: 0px; + padding: 6px 3px; + border-radius: 20px; + } + + #workspaces button { + font-size: 0px; + background-color: @base07; + padding: 0px 1px; + margin: 0px 4px; + border-radius: 20px; + transition: all 0.25s cubic-bezier(0.55, -0.68, 0.48, 1.682); + } + + #workspaces button.active { + font-size: 1px; + background-color: @base0E; + border-radius: 20px; + min-width: 30px; + background-size: 400% 400%; + } + + #workspaces button.empty { + font-size: 1px; + background-color: @base04; + } + + #window { + color: @base00; + background: radial-gradient(circle, @base05 0%, @base07 100%); + background-size: 400% 400%; + animation: gradient_f 40s ease-in-out infinite; + transition: all 0.3s cubic-bezier(0.55, -0.68, 0.48, 1.682); + } + + window#waybar.empty #window { + background: none; + background-color: transparent; + box-shadow: none; + } + + #battery { + margin-right: 15px; + background: @base0F; + /* background: linear-gradient(118deg, @base07 0%, @base0F 25%, @base07 50%, @base0F 75%, @base07 100%); */ + /* background: linear-gradient(118deg, @base07 5%, @base0F 5%, @base0F 20%, @base07 20%, @base07 40%, @base0F 40%, @base0F 60%, @base07 60%, @base07 80%, @base0F 80%, @base0F 95%, @base07 95%); */ + background: linear-gradient( + 118deg, + @base0B 5%, + @base0F 5%, + @base0F 20%, + @base0B 20%, + @base0B 40%, + @base0F 40%, + @base0F 60%, + @base0B 60%, + @base0B 80%, + @base0F 80%, + @base0F 95%, + @base0B 95% + ); + background-size: 200% 300%; + animation: gradient_f_nh 4s linear infinite; + color: @base01; + } + + #battery.charging, + #battery.plugged { + background: linear-gradient( + 118deg, + @base0E 5%, + @base0D 5%, + @base0D 20%, + @base0E 20%, + @base0E 40%, + @base0D 40%, + @base0D 60%, + @base0E 60%, + @base0E 80%, + @base0D 80%, + @base0D 95%, + @base0E 95% + ); + background-size: 200% 300%; + animation: gradient_rv 4s linear infinite; + } + + #battery.full { + background: linear-gradient( + 118deg, + @base0E 5%, + @base0D 5%, + @base0D 20%, + @base0E 20%, + @base0E 40%, + @base0D 40%, + @base0D 60%, + @base0E 60%, + @base0E 80%, + @base0D 80%, + @base0D 95%, + @base0E 95% + ); + background-size: 200% 300%; + animation: gradient_rv 20s linear infinite; + } + + #tray > .passive { + -gtk-icon-effect: dim; + } + + #tray > .needs-attention { + -gtk-icon-effect: highlight; + } + ''; + ".config/waybar/animation.css".text = + # css + '' + @keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 30%; + } + 100% { + background-position: 0% 50%; + } + } + + @keyframes gradient_f { + 0% { + background-position: 0% 200%; + } + 50% { + background-position: 200% 0%; + } + 100% { + background-position: 400% 200%; + } + } + + @keyframes gradient_f_nh { + 0% { + background-position: 0% 200%; + } + 100% { + background-position: 200% 200%; + } + } + + @keyframes gradient_rv { + 0% { + background-position: 200% 200%; + } + 100% { + background-position: 0% 200%; + } + } + ''; + }; +} diff --git a/home/programs/desktop/scripts/change-wal-niri b/home/programs/desktop/scripts/change-wal-niri new file mode 100755 index 0000000..16611ec --- /dev/null +++ b/home/programs/desktop/scripts/change-wal-niri @@ -0,0 +1,9 @@ +#!/bin/sh + +wallpapers_dir="$HOME/Pictures/Wallpapers/generated" +wall=$(ls $wallpapers_dir | grep -v '\-blurred\.jpg$' | tofi) +if [ -z $wall ]; then + exit +fi +output=$(niri msg -j focused-output | jq -r .name) +swww img -o $output $wallpapers_dir/$wall --transition-type random --transition-duration 1 diff --git a/home/programs/desktop/scripts/change-wal-wayland b/home/programs/desktop/scripts/change-wal-wayland new file mode 100755 index 0000000..74f99ea --- /dev/null +++ b/home/programs/desktop/scripts/change-wal-wayland @@ -0,0 +1,7 @@ +#!/bin/sh + +wall=$(yad --file) +if [[ $wall == "" ]]; then + exit +fi +swww img $wall --transition-type random --transition-duration 1 diff --git a/home/programs/desktop/scripts/tofi/colorscheme b/home/programs/desktop/scripts/tofi/colorscheme new file mode 100755 index 0000000..1ddeaae --- /dev/null +++ b/home/programs/desktop/scripts/tofi/colorscheme @@ -0,0 +1,27 @@ +#!/bin/sh + +current_profile=~/.local/state/nix/profiles/home-manager +current_os_profile=/nix/var/nix/profiles/system +specialisations=$(find /nix/store/*home-manager-generation/ -lname "$(realpath $current_profile)") +os_specialisations=$(find /nix/store/*nixos-system* -lname "$(realpath $current_os_profile)") +if [ -z "$specialisations" ]; then + specialisations=$current_profile/specialisation +else + specialisations=$(dirname "$specialisations") +fi +if [ -z "$os_specialisations" ]; then + os_specialisations=$current_os_profile/specialisation +else + specialisations=$(dirname "$specialisations") +fi +colorscheme=$( (ls "$specialisations"; echo default) | tofi --require-match=false) + +if [ -z "$colorscheme" ]; then + exit 1 +fi + +if [ "$colorscheme" = "default" ]; then + colorscheme=".." +fi +"$specialisations/$colorscheme"/activate +doas "$os_specialisations/$colorscheme"/bin/switch-to-configuration switch diff --git a/home/programs/desktop/scripts/tofi/config b/home/programs/desktop/scripts/tofi/config new file mode 100755 index 0000000..dac9b9b --- /dev/null +++ b/home/programs/desktop/scripts/tofi/config @@ -0,0 +1,9 @@ +#!/bin/sh + +tofi_dmenu() { + tofi --prompt-text $1 --fuzzy-match true --terminal "kitty" +} + +tofi_full() { + tofi -c ~/.config/tofi/config-full --prompt-text $1 --fuzzy-match true --terminal "kitty" +} diff --git a/home/programs/desktop/scripts/tofi/confirm b/home/programs/desktop/scripts/tofi/confirm new file mode 100755 index 0000000..52d7858 --- /dev/null +++ b/home/programs/desktop/scripts/tofi/confirm @@ -0,0 +1,5 @@ +#!/bin/sh + +. ~/scripts/tofi/config + +printf "yes\nno" | tofi_full $1 diff --git a/home/programs/desktop/scripts/tofi/custom-command b/home/programs/desktop/scripts/tofi/custom-command new file mode 100755 index 0000000..bfc52cd --- /dev/null +++ b/home/programs/desktop/scripts/tofi/custom-command @@ -0,0 +1,18 @@ +#!/bin/sh + +tmpfile=~/scripts/tofi/stored_commands +stored=$(cat $tmpfile) + +command=$(echo "$stored" | tofi --require-match=false) + +if [ -z "$command" ]; then + exit 1 +fi + +exec $command & + +if grep -q "$command" "$tmpfile"; then + exit 1 +fi + +echo "$command" >> $tmpfile diff --git a/home/programs/desktop/scripts/tofi/mode b/home/programs/desktop/scripts/tofi/mode new file mode 100755 index 0000000..f5de167 --- /dev/null +++ b/home/programs/desktop/scripts/tofi/mode @@ -0,0 +1,9 @@ +#!/bin/sh + +. ~/scripts/tofi/config + +entry=$(printf "power-saver\nbalanced\nperformance" | tofi_full "mode") +entry=$(echo $entry | cut -d' ' -f2 ) + +notify-send "Power Mode" "Setting mode to $entry" +doas powerprofilesctl set $entry diff --git a/home/programs/desktop/scripts/tofi/powermenu b/home/programs/desktop/scripts/tofi/powermenu new file mode 100755 index 0000000..2e6f072 --- /dev/null +++ b/home/programs/desktop/scripts/tofi/powermenu @@ -0,0 +1,21 @@ +#!/bin/sh + +. ~/scripts/tofi/config + +entry=$(printf "⏻ poweroff\n reboot\n mode\n exit\n suspend" | tofi_full "power") +entry=$(echo $entry | cut -d' ' -f2 ) + +if [ "$entry" = "poweroff" ] || [ "$entry" = "reboot" ]; then + if [ "$(~/scripts/tofi/confirm "confirm")" = "yes" ]; then + doas $entry + fi +elif [ "$entry" = "suspend" ]; then + systemctl suspend +elif [ "$entry" = "mode" ]; then + ~/scripts/tofi/mode +elif [ "$entry" = "exit" ]; then + if [ "$(~/scripts/tofi/confirm "exit?")" = "yes" ]; then + doas killall swhkd swhks + wayland-logout + fi +fi diff --git a/home/programs/desktop/tofi.nix b/home/programs/desktop/tofi.nix new file mode 100644 index 0000000..81fea61 --- /dev/null +++ b/home/programs/desktop/tofi.nix @@ -0,0 +1,84 @@ +{ + config, + pkgs, + ... +}: +{ + home.packages = with pkgs; [ + tofi + ]; + xdg.configFile."tofi/config".text = '' + # Font + # font = Fredoka medium + font = Hug Me Tight Regular + font-size = 10 + + # Window Style + horizontal = true + anchor = top + width = 98% + height = 40 + margin-top = 10 + margin-left = 1% + margin-right = 1% + + outline-width = 0 + border-width = 0 + corner-radius = 12 + min-input-width = 60 + result-spacing = 15 + padding-top = 7 + padding-bottom = 0 + padding-left = 10 + padding-right = 0 + + # Text style + prompt-text = "Can I have a" + prompt-padding = 15 + + background-color = #${config.lib.stylix.colors.base00} + text-color = #${config.lib.stylix.colors.base05} + + prompt-color = #${config.lib.stylix.colors.base00} + prompt-background = #${config.lib.stylix.colors.base0B} + prompt-background-padding = 2, 5 + prompt-background-corner-radius = 6 + + input-color = #${config.lib.stylix.colors.base00} + input-background = #${config.lib.stylix.colors.base07} + input-background-padding = 2, 5 + input-background-corner-radius = 6 + + alternate-result-color = #${config.lib.stylix.colors.base00} + alternate-result-background = #${config.lib.stylix.colors.base0A} + alternate-result-background-padding = 2, 5 + alternate-result-background-corner-radius = 6 + + selection-color = #${config.lib.stylix.colors.base00} + selection-background = #${config.lib.stylix.colors.base0E} + selection-background-padding = 2, 5 + selection-background-corner-radius = 6 + selection-match-color = #${config.lib.stylix.colors.base00} + + clip-to-padding = false + + history = true + ''; + xdg.configFile."tofi/config-full".text = '' + width = 100% + height = 100% + border-width = 0 + outline-width = 0 + padding-left = 35% + padding-top = 35% + result-spacing = 25 + num-results = 5 + # font = Comic Mono Nerd Font + font = Hug Me Tight + prompt-padding = 15 + background-color = #${config.lib.stylix.colors.base00}aa + text-color = #${config.lib.stylix.colors.base05} + prompt-color = #${config.lib.stylix.colors.base0B} + selection-color = #${config.lib.stylix.colors.base0E} + ''; +} diff --git a/home/programs/documents/default.nix b/home/programs/documents/default.nix new file mode 100644 index 0000000..7d1d421 --- /dev/null +++ b/home/programs/documents/default.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: +{ + imports = [ + ./zathura.nix + ]; + home.packages = with pkgs; [ + libreoffice + ]; +} diff --git a/home/programs/documents/zathura.nix b/home/programs/documents/zathura.nix new file mode 100644 index 0000000..33e1ce9 --- /dev/null +++ b/home/programs/documents/zathura.nix @@ -0,0 +1,59 @@ +{ + config, + pkgs, + ... +}: +{ + xdg.configFile."zathura/stylix".text = '' + set default-fg "#${config.lib.stylix.colors.base05}" + set default-bg "#${config.lib.stylix.colors.base00}" + + set completion-bg "#${config.lib.stylix.colors.base02}" + set completion-fg "#${config.lib.stylix.colors.base05}" + set completion-highlight-bg "#${config.lib.stylix.colors.base01}" + set completion-highlight-fg "#${config.lib.stylix.colors.base05}" + set completion-group-bg "#${config.lib.stylix.colors.base02}" + set completion-group-fg "#${config.lib.stylix.colors.base0D}" + + set statusbar-fg "#${config.lib.stylix.colors.base05}" + set statusbar-bg "#${config.lib.stylix.colors.base02}" + + set notification-bg "#${config.lib.stylix.colors.base02}" + set notification-fg "#${config.lib.stylix.colors.base05}" + set notification-error-bg "#${config.lib.stylix.colors.base02}" + set notification-error-fg "#${config.lib.stylix.colors.base08}" + set notification-warning-bg "#${config.lib.stylix.colors.base02}" + set notification-warning-fg "#${config.lib.stylix.colors.base0A}" + + set inputbar-fg "#${config.lib.stylix.colors.base05}" + set inputbar-bg "#${config.lib.stylix.colors.base02}" + + set recolor-lightcolor "#${config.lib.stylix.colors.base00}" + set recolor-darkcolor "#${config.lib.stylix.colors.base05}" + + set index-fg "#${config.lib.stylix.colors.base05}" + set index-bg "#${config.lib.stylix.colors.base00}" + set index-active-fg "#${config.lib.stylix.colors.base05}" + set index-active-bg "#${config.lib.stylix.colors.base02}" + + set render-loading-bg "#${config.lib.stylix.colors.base00}" + set render-loading-fg "#${config.lib.stylix.colors.base05}" + + set highlight-color "#${config.lib.stylix.colors.base01}" + set highlight-fg "#${config.lib.stylix.colors.base0E}" + set highlight-active-color "#${config.lib.stylix.colors.base0E}" + ''; + programs.zathura = { + enable = true; + package = pkgs.zathura; + extraConfig = '' + include stylix + + set selection-clipboard clipboard + + set recolor true + + set font 'Comic Code' + ''; + }; +} diff --git a/home/programs/network/clash-meta.nix b/home/programs/network/clash-meta.nix new file mode 100644 index 0000000..863b525 --- /dev/null +++ b/home/programs/network/clash-meta.nix @@ -0,0 +1,6 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + clash-meta + ]; +} diff --git a/home/programs/network/default.nix b/home/programs/network/default.nix new file mode 100644 index 0000000..f0d285f --- /dev/null +++ b/home/programs/network/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./clash-meta.nix + ./zjuconnect.nix + ]; +} diff --git a/home/programs/network/zjuconnect.nix b/home/programs/network/zjuconnect.nix new file mode 100644 index 0000000..e002731 --- /dev/null +++ b/home/programs/network/zjuconnect.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: +let + zju-connect = import ../../../pkgs/zju-connect.nix { + inherit (pkgs) lib buildGoModule fetchFromGitHub; + }; + zjuconnect = pkgs.writeShellScriptBin "zjuconnect" ''exec ${zju-connect}/bin/zju-connect --config ~/.config/zju-connect/config.toml''; +in +{ + home.packages = [ + zju-connect + zjuconnect + ]; +} diff --git a/home/programs/shell/default.nix b/home/programs/shell/default.nix new file mode 100644 index 0000000..0e7796f --- /dev/null +++ b/home/programs/shell/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./fish.nix + ./starship.nix + ]; +} diff --git a/home/programs/shell/fish.nix b/home/programs/shell/fish.nix new file mode 100644 index 0000000..5bb60e7 --- /dev/null +++ b/home/programs/shell/fish.nix @@ -0,0 +1,50 @@ +{ pkgs, ... }: +{ + programs = { + fish = { + enable = true; + shellAbbrs = { + nixu = "nh os switch --ask"; + homeu = "nh home switch --ask"; + nixc = "doas systemctl start nh-clean.service"; + vim = "nvim"; + cd = "z"; + }; + shellAliases = { + "ls" = "exa"; + "l" = "exa -lah --icons=auto"; + }; + shellInit = '' + zoxide init fish | source + export PATH="$HOME/.local/bin:$HOME/.juliaup/bin:$PATH" + ''; + plugins = with pkgs.fishPlugins; [ + { + name = "puffer"; + src = puffer.src; + } + { + name = "pisces"; + src = pisces.src; + } + ]; + functions = { + fish_greeting = ""; + # fish_command_not_found = + # /* + # fish + # */ + # '' + # function fish_command_not_found + # if test -e /run/.containerenv -o -e /.dockerenv + # distrobox-host-exec $argv + # else + # __fish_default_command_not_found_handler $argv + # end + # end + # ''; + }; + }; + }; + programs.man.generateCaches = false; +} diff --git a/home/programs/shell/starship.nix b/home/programs/shell/starship.nix new file mode 100644 index 0000000..927ad3a --- /dev/null +++ b/home/programs/shell/starship.nix @@ -0,0 +1,5 @@ +{ + programs.starship = { + enable = true; + }; +} diff --git a/home/programs/social/default.nix b/home/programs/social/default.nix new file mode 100644 index 0000000..b296a74 --- /dev/null +++ b/home/programs/social/default.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + element-desktop + telegram-desktop + ]; +} diff --git a/home/programs/study/default.nix b/home/programs/study/default.nix new file mode 100644 index 0000000..494bfdb --- /dev/null +++ b/home/programs/study/default.nix @@ -0,0 +1,6 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs; [ + pspp + ]; +} diff --git a/home/programs/terminal/default.nix b/home/programs/terminal/default.nix new file mode 100644 index 0000000..7b8cdbf --- /dev/null +++ b/home/programs/terminal/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./kitty.nix + ]; +} diff --git a/home/programs/terminal/kitty.nix b/home/programs/terminal/kitty.nix new file mode 100644 index 0000000..b3a852b --- /dev/null +++ b/home/programs/terminal/kitty.nix @@ -0,0 +1,38 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + programs.kitty = { + enable = true; + shellIntegration.enableFishIntegration = true; + settings = { + window_padding_width = "10 20 10 20"; + cursor_trail = 1; + cursor_trail_start_threshold = 0; + }; + extraConfig = '' + map ctrl+shift+p kitten hints --type path --program @ + map ctrl+shift+s kitten hints --type word --program @ + map ctrl+shift+l kitten hints --type line --program @ + + symbol_map U+4E00-U+9FFF LXGW WenKai Mono + symbol_map U+3400-U+4DBF LXGW WenKai Mono + symbol_map U+20000-U+2A6DF LXGW WenKai Mono + symbol_map U+2A700-U+2B73F LXGW WenKai Mono + symbol_map U+2B740-U+2B81F LXGW WenKai Mono + symbol_map U+2B820-U+2CEAF LXGW WenKai Mono + symbol_map U+2CEB0-U+2EBEF LXGW WenKai Mono + symbol_map U+30000-U+3134F LXGW WenKai Mono + symbol_map U+F900-U+FAFF LXGW WenKai Mono + symbol_map U+2F800-U+2FA1F LXGW WenKai Mono + ''; + }; + stylix.targets.kitty.enable = true; + home.packages = with pkgs; [ + termpdfpy + mdcat + ]; +} diff --git a/home/programs/utils/default.nix b/home/programs/utils/default.nix new file mode 100644 index 0000000..e927f0c --- /dev/null +++ b/home/programs/utils/default.nix @@ -0,0 +1,49 @@ +{ + pkgs, + ... +}: +{ + home.packages = with pkgs; [ + zoxide + gnome-tweaks + networkmanagerapplet + wayland-logout + wl-clipboard + sd + socat + pandoc + typst + du-dust + killall + htop + gparted + gimp + kdePackages.kdenlive + tesseract # ocr + marp-cli + appimage-run + audiowaveform + papers + nom + yad + pcmanfm + yazi + ydotool + jq + scrcpy + upscaler + direnv + entr + lutgen + imagemagick + ffmpeg + nurl + wl-color-picker + matugen + ]; + imports = [ + ./eye-candy.nix + ./obs.nix + ./music.nix + ]; +} diff --git a/home/programs/utils/eye-candy.nix b/home/programs/utils/eye-candy.nix new file mode 100644 index 0000000..ef58e27 --- /dev/null +++ b/home/programs/utils/eye-candy.nix @@ -0,0 +1,32 @@ +{ + pkgs, + config, + ... +}: +let + edenfetch = import ../../../pkgs/edenfetch.nix { inherit (pkgs) lib fetchFromGitHub rustPlatform; }; +in +{ + home.packages = with pkgs; [ + cmatrix + cbonsai + edenfetch + ]; + programs.fastfetch.enable = true; + xdg.configFile."fastfetch/config.jsonc".source = ./fastfetch.jsonc; + home.file."Pictures/face.jpg".source = import ../../../lib/wallpaper/goNord.nix { + inherit + pkgs + config + ; + wallpaper = { + name = "face"; + path = pkgs.fetchurl { + name = "face.jpg"; + url = "https://avatars.githubusercontent.com/EdenQwQ"; + sha256 = "1hxl459l3ni5yaj72dngy9wx9rd1yvb85v31nibv5mih4mp1p6cp"; + }; + + }; + }; +} diff --git a/home/programs/utils/fastfetch.jsonc b/home/programs/utils/fastfetch.jsonc new file mode 100644 index 0000000..a3dfdb8 --- /dev/null +++ b/home/programs/utils/fastfetch.jsonc @@ -0,0 +1,54 @@ +{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + "logo": { + "source": "~/Pictures/face.jpg", + "height": 9, + "padding": { + "top": 1, + "right": 1, + }, + }, + "display": { + "separator": "", + }, + "modules": [ + "break", + { + "type": "os", + "key": " OS ~ ", + "keyColor": "32", + }, + { + "type": "kernel", + "key": " KR ~ ", + "keyColor": "32", + }, + { + "type": "shell", + "key": " SH ~ ", + "keyColor": "32", + }, + { + "type": "terminal", + "key": " TM ~ ", + "keyColor": "32", + }, + { + "type": "wm", + "key": " WM ~ ", + "keyColor": "32", + }, + "break", + { + "type": "custom", + "format": " \u001b[90m \u001b[31m \u001b[32m \u001b[33m \u001b[34m \u001b[35m \u001b[36m \u001b[37m", + }, + "break", + { + "type": "command", + "key": " ʕ•́ᴥ•̀ʔっ♡ ", + "keyColor": "35", + "text": "echo welcome to $(hostname), \u001b[35m$(whoami)", + }, + ], +} diff --git a/home/programs/utils/fum.json b/home/programs/utils/fum.json new file mode 100644 index 0000000..67f76cf --- /dev/null +++ b/home/programs/utils/fum.json @@ -0,0 +1,56 @@ +{ + "players": ["musicfox", "lollypop", "org.mpris.MediaPlayer2.mpv"], + "use_active_player": false, + "debug": false, + "width": 20, + "height": 22, + "keybinds": { + "esc;q": "quit()", + "h": "backward(1000)", + "l": "forward(1000)", + " ": "play_pause()", + "H": "prev()", + "L": "next()" + }, + "layout": [ + { "type": "cover-art" }, + { "type": "empty", "size": 2 }, + { + "type": "container", + "direction": "vertical", + "children": [ + { "type": "label", "text": "$title", "align": "center" }, + { "type": "label", "text": "$artists", "align": "center" }, + { + "type": "container", + "height": 1, + "flex": "space-around", + "children": [ + { "type": "button", "text": "󰒮", "action": "prev()" }, + { + "type": "button", + "text": "$status-icon", + "action": "play_pause()" + }, + { "type": "button", "text": "󰒭", "action": "next()" } + ] + }, + { "type": "empty", "size": 1 }, + { + "type": "progress", + "progress": { "char": "" }, + "empty": { "char": "" } + }, + { + "type": "container", + "height": 1, + "flex": "space-between", + "children": [ + { "type": "label", "text": "$position", "align": "left" }, + { "type": "label", "text": "$length", "align": "right" } + ] + } + ] + } + ] +} diff --git a/home/programs/utils/music.nix b/home/programs/utils/music.nix new file mode 100644 index 0000000..a78b4da --- /dev/null +++ b/home/programs/utils/music.nix @@ -0,0 +1,140 @@ +{ + config, + pkgs, + ... +}: +{ + home.packages = with pkgs; [ + go-musicfox + fum + ]; + xdg.configFile."go-musicfox/go-musicfox.ini".text = + # ini + '' + # 启动页配置 + [startup] + # 是否显示启动页 + show=true + # 启动页进度条是否有回弹效果 + progressOutBounce=true + # 启动页时长 + loadingSeconds=2 + # 启动页欢迎语 + welcome=musicfox + # 启动时自动签到 (网易云现在有检测,建议关闭) + signIn=false + # 启动时检查更新 + checkUpdate=false + + # 进度条配置 + [progress] + # 进度条已加载字符 + fullChar="" + fullCharWhenFirst="" + fullCharWhenLast="" + lastFullChar="" + # 进度条未加载字符 + emptyChar="." + emptyCharWhenFirst="." + emptyCharWhenLast="." + firstEmptyChar="." + + # 主页面配置 + [main] + # 是否显示标题 + showTitle=true + # 加载中提示 + loadingText=[加载中...] + # 歌曲音质,standard,higher,exhigh,lossless,hires + songLevel=higher + # 主题颜色 + # 随机 + # primaryColor=random + # 经典网易云音乐红 + primaryColor="#${config.lib.stylix.colors.base07}" + # windows,linux下的通知图标 + notifyIcon="logo.png" + # 是否显示歌词 + showLyric=true + # 歌词偏移 ms + lyricOffset=0 + # 显示歌词翻译 + showLyricTrans=false + # 是否显示通知信息 + showNotify=true + # 开启pprof, --pprof时会开启 + pprofPort=9876 + # altScreen显示模式 + altScreen=true + # 开启鼠标事件 + enableMouseEvent=true + # 双列显示,开启务必使用等宽字体 + doubleColumn=true + # 下载目录,默认为$${MUSICFOX_ROOT}/download + downloadDir= + # 文件名模板 + downloadFileNameTpl={{.SongName}}-{{.ArtistName}}.{{.SongType}} + # 缓存目录,默认为$${MUSICFOX_ROOT}/cache + # !!!注意!!! 如果使用mpd,mpd配置中的"music_directory"必须与cacheDir一致 + cacheDir= + # 缓存大小(以MB为单位),0为不使用缓存,-1为不限制,默认为0 + cacheLimit=0 + # 是否显示歌单下所有歌曲,默认不开启,仅获取歌单前1000首,开启后可能会占用更多内存(大量歌曲数据)和带宽(会同时发送多个请求获取歌单下歌曲数据) + showAllSongsOfPlaylist=false + + [global_hotkey] + # 全局快捷键 + # 格式:键=功能 (https://github.com/go-musicfox/go-musicfox/blob/master/internal/ui/event_handler.go#L15) + # ctrl+shift+space=toggle + + [autoplay] + # 是否开启自动播放,默认不开启 + autoPlay=false + # 自动播放歌单,dailyReco, like, name:歌单名, no(保持上次退出时的设置,无视offset),默认为dailyReco + autoPlayList=dailyReco + # 播放偏移,0为第一首,-1为最后一首,默认为0 + offset=0 + # 播放模式,listLoop, order, singleLoop, random(无视offset), intelligent(心动), last(上次退出时的模式),默认为last + playMode=last + + [player] + # 播放引擎 beep / mpd(需要安装配置mpd) / osx(Mac才可用) + # 不填Mac默认使用osx,其他系统默认使用beep(推荐的配置) + #engine=beep + # beep使用的mp3解码器,可选:go-mp3, minimp3 (minimp3更少的CPU占用,但是稳定性不如go-mp3) + beepMp3Decoder=go-mp3 + + # mpd配置 + mpdBin=/usr/local/bin/mpd + # !!!注意!!! 一定要在配置文件中设置pid_file,否则在退出时不会kill掉mpd进程 + mpdConfigFile=/Users/anhoder/.mpd/mpd.conf + # tcp 或 unix + mpdNetwork=unix + # tcp时填写ip+port(例如:127.0.0.1:1234),unix时填写socket文件路径 + mpdAddr= + + [unm] + # UNM开关 + switch=true + # UNM源: kuwo,kugou,migu,qq + sources=kuwo,kugou,migu,qq + # UNM搜索其他平台限制 0-3 + searchLimit=0 + # 解除会员限制 + enableLocalVip=true + # 解除音质限制 + unlockSoundEffects=true + # QQ音乐cookie文件 + qqCookieFile= + ''; + + programs.cava = { + enable = true; + }; + stylix.targets.cava = { + enable = true; + rainbow.enable = true; + }; + + xdg.configFile."fum/config.jsonc".source = ./fum.json; +} diff --git a/home/programs/utils/obs.nix b/home/programs/utils/obs.nix new file mode 100644 index 0000000..30bd668 --- /dev/null +++ b/home/programs/utils/obs.nix @@ -0,0 +1,10 @@ +{ pkgs, ... }: +{ + programs.obs-studio = { + enable = true; + plugins = with pkgs.obs-studio-plugins; [ + wlrobs + obs-pipewire-audio-capture + ]; + }; +} diff --git a/home/tweaks/dconf.nix b/home/tweaks/dconf.nix new file mode 100644 index 0000000..b96548f --- /dev/null +++ b/home/tweaks/dconf.nix @@ -0,0 +1,8 @@ +{ + dconf.settings = { + "org/virt-manager/virt-manager/connections" = { + autoconnect = [ "qemu:///system" ]; + uris = [ "qemu:///system" ]; + }; + }; +} diff --git a/home/tweaks/default.nix b/home/tweaks/default.nix new file mode 100644 index 0000000..6f8aaf7 --- /dev/null +++ b/home/tweaks/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./gnome.nix + ./fcitx5.nix + ./dconf.nix + ./stylix.nix + ]; +} diff --git a/home/tweaks/fcitx5.nix b/home/tweaks/fcitx5.nix new file mode 100644 index 0000000..0c8db75 --- /dev/null +++ b/home/tweaks/fcitx5.nix @@ -0,0 +1,110 @@ +{ config, ... }: +{ + xdg.dataFile."fcitx5/themes/stylix/theme.conf".text = + # ini + '' + [Metadata] + Name=stylix + Version=0.2 + Author=justTOBBI and Isabelinc + ScaleWithDPI=True + + [InputPanel] + # 字体 + Font=Sans 13 + # 非选中候选字颜色 + #Blue + NormalColor=#${config.lib.stylix.colors.base0D} + # 选中候选字颜色 + #Peach + HighlightCandidateColor=#${config.lib.stylix.colors.base00} + # 高亮前景颜色(输入字符颜色) + #Peach + HighlightColor=#${config.lib.stylix.colors.base07} + # 输入字符背景颜色 + # Black3/surface0 + HighlightBackgroundColor=#${config.lib.stylix.colors.base02} + # + Spacing=3 + + [InputPanel/TextMargin] + # 候选字对左边距 + Left=10 + # 候选字对右边距 + Right=10 + # 候选字向上边距 + Top=6 + # 候选字向下边距 + Bottom=6 + + [InputPanel/Background] + #Black3/surface0 + Color=#${config.lib.stylix.colors.base01} + #Black3/surface0 + BorderColor=#${config.lib.stylix.colors.base0E} + BorderWidth=0 + + [InputPanel/Background/Margin] + Left=2 + Right=2 + Top=2 + Bottom=2 + + [InputPanel/Highlight] + #Black3/surface0 + Color=#${config.lib.stylix.colors.base0E} + + [InputPanel/Highlight/Margin] + # 高亮区域左边距 + Left=10 + # 高亮区域右边距 + Right=10 + # 高亮区域上边距 + Top=7 + # 高亮区域下边距 + Bottom=7 + + [Menu] + Font=Sans 10 + #White/Text + NormalColor=#${config.lib.stylix.colors.base05} + #HighlightColor=#4c566a + Spacing=3 + + [Menu/Background] + #Black3/surface0 + Color=#${config.lib.stylix.colors.base02} + + [Menu/Background/Margin] + Left=2 + Right=2 + Top=2 + Bottom=2 + + [Menu/ContentMargin] + Left=2 + Right=2 + Top=2 + Bottom=2 + + [Menu/Highlight] + #Pink + Color=#${config.lib.stylix.colors.base0E} + + [Menu/Highlight/Margin] + Left=10 + Right=10 + Top=5 + Bottom=5 + + [Menu/Separator] + #Black2/base + Color=#${config.lib.stylix.colors.base00} + + [Menu/TextMargin] + Left=5 + Right=5 + Top=5 + Bottom=5 + ''; +} diff --git a/home/tweaks/gnome.nix b/home/tweaks/gnome.nix new file mode 100644 index 0000000..91be470 --- /dev/null +++ b/home/tweaks/gnome.nix @@ -0,0 +1,30 @@ +{ pkgs, ... }: +{ + home.packages = with pkgs.gnomeExtensions; [ + user-themes + blur-my-shell + desktop-cube + kimpanel + ]; + dconf.settings = { + "org/gnome/shell" = { + disable-user-extensions = false; + enabled-extensions = [ + "user-theme@gnome-shell-extensions.gcampax.github.com" + "blur-my-shell@aunetx" + "kimpanel@kde.org" + ]; + }; + "org/gnome/mutter" = { + "experimental-features" = [ "scale-monitor-framebuffer" ]; + }; + "org/gnome/desktop/wm/keybindings" = { + "close" = [ "q" ]; + }; + "org/gnome/settings-daemon.plugins.media-keys" = { + custom-keybindings = [ + "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/" + ]; + }; + }; +} diff --git a/home/tweaks/stylix.nix b/home/tweaks/stylix.nix new file mode 100644 index 0000000..a634f2d --- /dev/null +++ b/home/tweaks/stylix.nix @@ -0,0 +1,42 @@ +{ + pkgs, + lib, + config, + ... +}: +let + recolorScript = import ../../lib/colorScheme/recolor.nix { inherit pkgs config; }; +in +{ + stylix = { + autoEnable = false; + targets.gtk.enable = true; + targets.gtk.flatpakSupport.enable = true; + cursor = { + package = pkgs.graphite-cursors.overrideAttrs (oldAttrs: { + postInstall = recolorScript + (oldAttrs.postInstall or ""); + }); + name = "graphite-dark"; + size = 32; + }; + fonts = { + monospace.name = "ComicShannsMono Nerd Font"; + monospace.package = pkgs.nerd-fonts.comic-shanns-mono; + sizes.terminal = 13; + sansSerif.name = "LXGW WenKai"; + sansSerif.package = pkgs.lxgw-wenkai; + serif.name = "LXGW WenKai"; + serif.package = pkgs.lxgw-wenkai; + emoji.name = "Noto Color Emoji"; + emoji.package = pkgs.noto-fonts-color-emoji; + }; + iconTheme = { + enable = true; + package = pkgs.zafiro-icons.overrideAttrs (oldAttrs: { + postInstall = recolorScript + (oldAttrs.postInstall or ""); + }); + dark = "Zafiro-icons-Dark"; + light = "Zafiro-icons-Light"; + }; + }; +} diff --git a/hosts/default.nix b/hosts/default.nix new file mode 100644 index 0000000..8a8b98d --- /dev/null +++ b/hosts/default.nix @@ -0,0 +1,67 @@ +{ + inputs, + nixpkgs, + self, + ... +}: +let + sharedOSModules = [ + ../overlays + ../os + ../nix + inputs.stylix.nixosModules.stylix + inputs.niri.nixosModules.niri + ]; + sharedHomeModules = [ + ../overlays + ../home + ../nix/nixpkgs.nix + ../sharedConfig.nix + ../lib/colorScheme/buildColorScheme.nix + ../lib/wallpaper/buildWallpaper.nix + inputs.nur.modules.homeManager.default + inputs.stylix.homeManagerModules.stylix + inputs.niri.homeModules.niri + inputs.nixvim.homeManagerModules.nixvim + ]; +in +{ + flake = + let + host = "eden-inspiron"; + user = "eden"; + in + { + nixosConfigurations.${host} = nixpkgs.lib.nixosSystem { + specialArgs = { + inherit + inputs + nixpkgs + self + host + user + ; + }; + modules = [ + ./inspiron/os.nix + ] ++ sharedOSModules; + }; + homeConfigurations."${user}@${host}" = inputs.home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages."x86_64-linux"; + extraSpecialArgs = { + inherit + inputs + self + host + user + ; + nixosVersion = "unstable"; + homeManagerVersion = "master"; + }; + modules = [ + ./inspiron/home.nix + ] ++ sharedHomeModules; + }; + + }; +} diff --git a/hosts/inspiron/hardware-configuration.nix b/hosts/inspiron/hardware-configuration.nix new file mode 100644 index 0000000..15da791 --- /dev/null +++ b/hosts/inspiron/hardware-configuration.nix @@ -0,0 +1,61 @@ +# 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" + "thunderbolt" + "vmd" + "nvme" + "usb_storage" + "usbhid" + "sd_mod" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/38925c0c-2f51-4522-9a9f-569ebc84c651"; + fsType = "btrfs"; + options = [ "subvol=@" ]; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/2288-DDD1"; + fsType = "vfat"; + }; + + fileSystems."/home" = { + device = "/dev/disk/by-uuid/65cff70e-3574-4e5a-9bbd-e3e9be84ba7c"; + fsType = "btrfs"; + }; + + swapDevices = [ + { + device = "/var/lib/swapfile"; + size = 5 * 1024; + } + ]; + + # 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.wlp0s20f3.useDHCP = lib.mkDefault true; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/inspiron/home.nix b/hosts/inspiron/home.nix new file mode 100644 index 0000000..c37317c --- /dev/null +++ b/hosts/inspiron/home.nix @@ -0,0 +1,32 @@ +{ + monitors = { + "eDP-1" = { + isMain = true; + scale = 1.6; + mode = { + width = 2240; + height = 1400; + refresh = 60.0; + }; + position = { + x = 1067; + y = 0; + }; + rotation = 0; + }; + "HDMI-A-1" = { + scale = 1.5; + mode = { + width = 2560; + height = 1600; + refresh = 60.0; + }; + position = { + x = 0; + y = 0; + }; + rotation = 90; + }; + }; + home.stateVersion = "23.11"; +} diff --git a/hosts/inspiron/os.nix b/hosts/inspiron/os.nix new file mode 100644 index 0000000..527df34 --- /dev/null +++ b/hosts/inspiron/os.nix @@ -0,0 +1,6 @@ +{ host, ... }: +{ + imports = [ ./hardware-configuration.nix ]; + system.stateVersion = "23.11"; + networking.hostName = host; +} diff --git a/lib/colorScheme/buildColorScheme.nix b/lib/colorScheme/buildColorScheme.nix new file mode 100644 index 0000000..33ba417 --- /dev/null +++ b/lib/colorScheme/buildColorScheme.nix @@ -0,0 +1,142 @@ +{ + pkgs, + config, + lib, + ... +}: +let + matugenOptions = lib.types.submodule { + options = { + image = lib.mkOption { + type = lib.types.either lib.types.str lib.types.path; + description = "Path to the image"; + }; + polarity = lib.mkOption { + type = lib.types.enum [ + "dark" + "light" + ]; + description = "Polarity of the color scheme (dark or light)"; + default = "dark"; + }; + scheme = lib.mkOption { + type = lib.types.str; + description = "The material you scheme to use"; + default = "scheme-tonal-spot"; + }; + }; + }; + + colorScheme = lib.types.submodule { + options = { + name = lib.mkOption { + type = lib.types.str; + description = "Name of the color scheme"; + }; + isDefault = lib.mkOption { + type = lib.types.bool; + description = "Whether the color scheme is the default"; + default = false; + }; + matugen = lib.mkOption { + type = lib.types.nullOr matugenOptions; + description = "Matugen options"; + default = null; + }; + }; + }; + + convertColorScheme = + colorScheme: + if builtins.typeOf colorScheme == "string" then + { + name = colorScheme; + isDefault = false; + matugen = null; + } + else + colorScheme; + + wallpapers = config.wallpapers; + + matugenToBase16 = + colorScheme: + let + inherit (colorScheme) name matugen; + inherit (matugen) polarity scheme; + image = + if builtins.typeOf matugen.image == "path" then + matugen.image + else + pkgs.fetchurl { + inherit (builtins.filter (wallpaper: wallpaper.name == matugen.image) wallpapers |> builtins.head) + name + url + sha256 + ; + }; + in + pkgs.runCommand "${name}.yaml" { buildInputs = [ pkgs.matugen ]; } + # bash + '' + ${pkgs.python3}/bin/python ${./matu2base16.py} ${image} \ + --name ${name} --polarity ${polarity} --type ${scheme} --output $out + ''; + + buildColorScheme = + colorScheme: + let + inherit (colorScheme) + name + isDefault + matugen + ; + forceOrDefault = if isDefault then lib.mkDefault else lib.mkForce; + in + { + base16Scheme = + if matugen != null then + "${matugenToBase16 colorScheme}" + else + forceOrDefault "${pkgs.base16-schemes}/share/themes/${name}.yaml"; + polarity = + if matugen != null then + matugen.polarity + else if builtins.fromJSON config.lib.stylix.colors.base00-dec-r < 0.5 then + forceOrDefault "dark" + else + forceOrDefault "light"; + }; + + buildSpecialisation = + colorScheme: + let + inherit (colorScheme) name; + in + { + inherit name; + value.configuration = { + stylix = buildColorScheme colorScheme; + xdg.dataFile."home-manager/specialisation".text = name; + }; + }; +in +{ + options.colorSchemes = lib.mkOption { + type = lib.types.listOf (lib.types.either colorScheme lib.types.str); + description = "List of colorschemes"; + }; + + config = + let + colorSchemes = config.colorSchemes |> map convertColorScheme; + in + { + stylix = { + enable = true; + } // (builtins.filter (c: c.isDefault) colorSchemes |> builtins.head |> buildColorScheme); + + specialisation = + builtins.filter (c: !c.isDefault) colorSchemes |> map buildSpecialisation |> builtins.listToAttrs; + }; +} diff --git a/lib/colorScheme/matu2base16.py b/lib/colorScheme/matu2base16.py new file mode 100644 index 0000000..47d4cce --- /dev/null +++ b/lib/colorScheme/matu2base16.py @@ -0,0 +1,72 @@ +import json +import argparse +import subprocess + +base16_map = { + "base00": "surface_container", + "base01": "surface_container_high", + "base02": "surface_container_highest", + "base03": "outline", + "base04": "on_surface_variant", + "base05": "on_background", + "base06": "on_error_container", + "base07": "on_tertiary_container", + "base08": "error", + "base09": "on_secondary_container", + "base0A": "on_primary_container", + "base0B": "primary", + "base0C": "secondary", + "base0D": "surface_tint", + "base0E": "tertiary", + "base0F": "tertiary_fixed_dim", +} + +parser = argparse.ArgumentParser( + description="Create base16 yaml file from matugen output" +) +parser.add_argument("image", help="The image to generate the colors from") +parser.add_argument("--name", "-n", help="The name of the colorscheme") +parser.add_argument("--polarity", "-p", help="Dark or light", default="dark") +parser.add_argument( + "--type", "-t", help="The type of the colorscheme", default="scheme-tonal-spot" +) +parser.add_argument("--output", "-o", help="The output file", default="base16.yaml") +args = parser.parse_args() +image = args.image +name = args.name +polarity = args.polarity +scheme_type = args.type +output = args.output + +matugen_output = subprocess.run( + [ + "matugen", + "image", + image, + "--dry-run", + "--type", + scheme_type, + "--json", + "hex", + ], + capture_output=True, + text=True, +).stdout + +colors = json.loads(matugen_output)["colors"][polarity] + +base16_colors = { + name: f'"{colors[hex].strip("\'")}"' for name, hex in base16_map.items() +} + +yaml_content = f"""system: "base16" +name: "{name}" +author: "matugen" +variant: "{polarity}" +palette: +""" + "\n".join( + [f" {k}: {v}" for k, v in base16_colors.items()] +) + +with open(output, "w") as f: + f.write(yaml_content) diff --git a/lib/colorScheme/recolor.nix b/lib/colorScheme/recolor.nix new file mode 100644 index 0000000..6e26401 --- /dev/null +++ b/lib/colorScheme/recolor.nix @@ -0,0 +1,44 @@ +{ pkgs, config }: +let + cfg.recolor = { + mode = "palette"; + colors = config.lib.stylix.colors.withHashtag.toList; + smooth = true; + }; + basic-colormath = pkgs.python3.pkgs.buildPythonPackage rec { + pname = "basic-colormath"; + version = "0.5.0"; + pyproject = true; + + src = pkgs.fetchPypi { + inherit version; + pname = "basic_colormath"; + hash = "sha256-p/uNuNg5kqKIkeMmX5sWY8umGAg0E4/otgQxhzIuo0E="; + }; + + propagatedBuildInputs = with pkgs.python3.pkgs; [ + setuptools + setuptools-scm + pillow + ]; + }; + pythonEnv = pkgs.python3.withPackages ( + ps: with ps; [ + basic-colormath + colormath + tqdm + pillow + ] + ); +in +with cfg.recolor; +'' + ${pythonEnv}/bin/python ${./recolor.py} --src $out/share/icons \ + --smooth '${toString smooth}' \ + ${ + if cfg.recolor.mode == "monochrome" then + "--monochrome '${builtins.concatStringsSep "," cfg.recolor.colors}'" + else + "--palette ''${builtins.concatStringsSep "," cfg.recolor.colors}''" + } +'' diff --git a/lib/colorScheme/recolor.py b/lib/colorScheme/recolor.py new file mode 100644 index 0000000..05cf977 --- /dev/null +++ b/lib/colorScheme/recolor.py @@ -0,0 +1,680 @@ +# pylint: skip-file +# pylint: disable=W,C,R +# pylint: disable-all +# type: ignore + +# Desc: A program for recoloring icon packs, themes and wallpapers. For NovaOS. +# Auth: Nicklas Vraa + +from typing import Annotated,List, Set, Tuple, Dict, Optional +from tqdm import tqdm +# from basic_colormath.type_hints import RGB, Lab +from basic_colormath.distance import rgb_to_lab, get_delta_e_lab +from PIL import Image, ImageDraw # noqa +import os, re, shutil, json, subprocess, argparse, random # noqa + +# Using custom type hints as the default ones in basic_colormath.type_hits arent compatible past python 3.8 +RGB = Annotated[tuple[float, float, float], ([0, 255], [0, 255], [0, 255])] +Lab = Annotated[tuple[float, float, float], ([0, 100], [-128, 127], [-128, 127])] +def LabColor(L:float, a:float, b:float) -> Lab: + return L, a, b +def sRGBColor(r:float, g:float, b:float) -> RGB: + return r,g,b + +# Global constants ------------------------------------------------------------- + +# A dynamic dictionary to avoid multiple color conversions. +hex_to_lab_dict = { + "#ffffff": LabColor(9341.568974319263, -0.037058350415009045, -0.6906417562959177), # White. + "#000000": LabColor(0,0,0) # Black. +} + +name_to_hex_dict = { + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgreen": "#006400", + "darkgrey": "#a9a9a9", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkslategrey": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dimgrey": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "goldenrod": "#daa520", + "gold": "#ffd700", + "gray": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "grey": "#808080", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred": "#cd5c5c", + "indigo": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavenderblush": "#fff0f5", + "lavender": "#e6e6fa", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgray": "#d3d3d3", + "lightgreen": "#90ee90", + "lightgrey": "#d3d3d3", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightslategrey": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370db", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#db7093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "rebeccapurple": "#663399", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "slategrey": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32" +} + +# Basic utility ---------------------------------------------------------------- + +def expand_path(path:str) -> str: + """ Returns the absolute version of the given path, and expands unix notation like tilde for home folder. """ + return os.path.abspath(os.path.expanduser(path)) + +def is_empty(list:List) -> bool: + """ Returns true if given list is empty, else false. """ + return len(list) == 0 + +def load_json_file(path:str) -> Dict: + """ Return object defined in given json file. """ + with open(path, 'r') as file: + obj = json.load(file) + return obj + +def check_path(path:str) -> None: + if not os.path.exists(expand_path(path)): + raise Exception("Invalid path: " + path) + +def svg_to_png(src_path:str, dest_path:str, width:int = 300) -> None: + """ Generate pngs at given destination path from a given source folder with a given width. """ + + src_path = expand_path(src_path) + dest_path = expand_path(dest_path) + + try: + subprocess.run(['inkscape', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except FileNotFoundError: + raise RuntimeError("Inkscape is not installed.") + + os.makedirs(dest_path, exist_ok=True) + svgs = [file for file in os.listdir(src_path) if file.endswith('.svg')] + + for svg in svgs: + svg_path = os.path.join(src_path, svg) + png = os.path.splitext(svg)[0] + '.png' + png_path = os.path.join(dest_path, png) + command = ['inkscape', svg_path, '-o', png_path, '-w', str(width)] + subprocess.run(command) + +# Color conversion ------------------------------------------------------------- + +def hex_to_rgb(hex:str) -> Tuple[int,int,int]: + """ Converts 6-digit hexadecimal color to rgb color. """ + return int(hex[1:3], 16), int(hex[3:5], 16), int(hex[5:7], 16) + +def hex_to_hsl(hex:str) -> Tuple[float,float,float]: + """ Converts 6-digit hexadecimal color to hsl color. """ + return rgb_to_hsl(hex_to_rgb(hex)) + +def hex_to_gray(hex:str) -> str: + """ Grayscales a given 6-digit hexadecimal color. """ + r, g, b = hex_to_rgb(hex) + return '#' + format(int(0.21*r + 0.72*g + 0.07*b), '02x')*3 + +def rgb_to_hex(rgb:Tuple[int,int,int]) -> str: + """ Converts rgb color to 6-digit hexadecimal color. """ + r, g, b = rgb + return "#{:02x}{:02x}{:02x}".format(r, g, b) + +def rgba_to_hex(rgba:Tuple[int,int,int,Optional[float]]) -> str: + """ Converts rgba color to 8-digit hexadecimal color. """ + r, g, b = rgba[:3] + hex = "#{:02X}{:02X}{:02X}".format(r, g, b) + + if len(rgba) > 3: + if rgba[3] != 1.0: + hex += format(int(rgba[3] * 255), '02X') + + return hex + +def rgb_to_hsl(rgb:Tuple[int,int,int]) -> Tuple[float,float,float]: + """ Converts rgb color to hsl color. """ + r, g, b = rgb + r /= 255.0; g /= 255.0; b /= 255.0 + max_val = max(r, g, b); min_val = min(r, g, b) + h = s = l = (max_val + min_val) / 2.0 + + if max_val == min_val: + h = s = 0 + else: + d = max_val - min_val + s = d / (2.0 - max_val - min_val) + + if max_val == r: h = (g - b) / d + (6.0 if g < b else 0.0) + elif max_val == g: h = (b - r) / d + 2.0 + else: h = (r - g) / d + 4.0 + h /= 6.0 + + return h, s, l + +def rgb_to_gray(rgb:Tuple[int,int,int]) -> Tuple[int,int,int]: + """ Grayscales a given rgb color. """ + r, g, b = rgb + weighed_avg = int(0.21*r + 0.72*g + 0.07*b) + return weighed_avg, weighed_avg, weighed_avg + +def hsl_to_rgb(hsl:Tuple[float,float,float]) -> Tuple[int,int,int]: + """ Converts hsl color to rgb color. """ + h, s, l = hsl + + if s == 0: + r = g = b = l + else: + if l < 0.5: q = l * (1 + s) + else: q = l + s - l * s + p = 2 * l - q + r = hue_to_rgb(p, q, h + 1 / 3) + g = hue_to_rgb(p, q, h) + b = hue_to_rgb(p, q, h - 1 / 3) + + return int(round(r * 255)), int(round(g * 255)), int(round(b * 255)) + +def hue_to_rgb(p:float, q:float, t:float) -> float: + """ Converts hue to rgb values. Only used by the hsl_to_rgb function. """ + if t < 0: t += 1 + if t > 1: t -= 1 + if t < 1 / 6: return p + (q - p) * 6 * t + if t < 1 / 2: return q + if t < 2 / 3: return p + (q - p) * (2 / 3 - t) * 6 + return p + +def norm_hsl(h:int, s:int, l:int) -> Tuple[float,float,float]: + """ Normalize hsl color values. """ + return h/360, s/100, l/100 + +# Preprocessing ---------------------------------------------------------------- + +def expand_css_rgb(match) -> str: + """ Used by the css_to_hex function. """ + return rgb_to_hex(( + int(match.group(1)), int(match.group(2)), + int(match.group(3))) + ) + +def css_to_hex(text:str) -> str: + """ Returns the given string with css rgba functions and named colors substituted for their corresponding hexadecimal codes. """ + + text = re.sub(r"rgba\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d.]+)\)", + expand_css_rgb, text) + + text = re.sub(r"rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)", + expand_css_rgb, text) + + for key in name_to_hex_dict: + text = re.sub(key + r"\b", name_to_hex_dict[key], text) + + return text + +# Post-processing -------------------------------------------------------------- + +def hex_to_rgba(match) -> str: + """ Converts 8-digit hexadecimal code to rgba function. """ + hex = match.group(1) + + return f"rgba({int(hex[1:3], 16)}, {int(hex[3:5], 16)}, {int(hex[5:7], 16)}, {int(hex[7:9], 16) / 255.0:.2f})" + +def hex_to_css(text:str) -> str: + """ Convert 8-digit hexadecimal color codes to css rgba color functions. Needed when a css interpreter does not recognize the alpha-channel when reading hexadecimal color codes. """ + + return re.sub(r"(#[0-9a-fA-F]{8})", hex_to_rgba, text) + +def expand_all_hex(text:str) -> str: + """Expand all 3-digit hexadecimal codes in the input string to 6 digits.""" + + return re.sub( + r"((? Dict[str,Lab]: + """ Returns a dictionary mapping hexadecimal colors to lab colors. """ + palette_dict = {} + + for color in colors: + r, g, b = hex_to_rgb(color) + palette_dict[color] = rgb_to_lab(sRGBColor(r,g,b)) + + return palette_dict + +def get_input_colors(resource): + """ Returns an HSL tuple, or a palette of colors, or a color mapping, depending on the input, as well as a string indicating which one, and if smoothing should be applied to pngs/jpgs. """ + + # If resource is an hsl color. + if isinstance(resource, tuple) and len(resource) == 3: + return resource, False, "color" + + else: + # If resource is a path to a resource, first unpack. + if type(resource) is str: + resource = load_json_file(resource) + + if resource["type"] == "palette": + return generate_palette_dict(resource["colors"]), resource["smooth"], "palette" + + elif resource["type"] == "mapping": + return resource["map"], resource["smooth"], "mapping" + +def get_file_colors(text:str) -> Set[str]: + """ Return a set of all unique colors within a given string representing an svg-file. """ + + colors = set() + matches = re.findall(r"#[A-Fa-f0-9]{6}", text) + + for match in matches: + colors.add(match) + + return colors + +def closest_match(color:str, palette:Dict[str,Lab]) -> str: + """ Compare the similarity of colors in the CIELAB colorspace. Return the closest match, i.e. the palette entry with the smallest euclidian distance to the given color. """ + + closest_color = None + min_distance = float('inf') + + for entry in palette: + + # Prior dictionary lookup and update. + lab_color = hex_to_lab_dict.get(color) + + if lab_color is None: + r, g, b = hex_to_rgb(color) + lab_color = rgb_to_lab(sRGBColor(r,g,b)) + hex_to_lab_dict[color] = lab_color + + distance = get_delta_e_lab(lab_color, palette[entry]) + + if distance < min_distance: + min_distance = distance + closest_color = entry + + return closest_color + +# Pack management -------------------------------------------------------------- + +def get_paths(folder: str, exts: List[str]) -> List[str]: + """ Return paths of every file with the given extensions within a folder and its subfolders, excluding symbolic links. """ + + paths = [] + + for item in os.listdir(folder): + item_path = os.path.join(folder, item) + + if os.path.islink(item_path): # Link. + continue + + if os.path.isfile(item_path): # File. + for ext in exts: + if item.lower().endswith(ext): + paths.append(item_path) + + elif os.path.isdir(item_path): # Folder. + subfolder_paths = get_paths(item_path, exts) + paths.extend(subfolder_paths) + + return paths + +def copy_file_structure(src_path:str, dest_path:str) -> None: + """ Copies a directory tree, but changes symbolic links to point to files within the destination folder instead of the source. Assumes that no link points to files outside the source folder. """ + + shutil.rmtree(dest_path, ignore_errors=True) + shutil.copytree(src_path, dest_path, symlinks=True) + + for root, _, files in os.walk(dest_path): + for file in files: + file_path = os.path.join(root, file) + + if os.path.islink(file_path): + link_target = os.readlink(file_path) + + if not os.path.isabs(link_target): + continue + + # Make link relative and update. + link_base = os.path.dirname(file_path) + relative_target = os.path.relpath(link_target, link_base) + os.remove(file_path) + + # Replace root source folder with root destination folder. + relative_target = relative_target.replace(src_path, dest_path, 1) + os.symlink(relative_target, file_path) + +def rename_pack(src_path, dest_path:str, name:str) -> None: + """ If an index.theme file exists within the given folder, apply appropiate naming. """ + + index_path = os.path.join(dest_path, "index.theme") + + print(index_path) + if os.path.exists(index_path): + with open(index_path, 'r') as file: + text = file.read() + + text = re.sub(r"(Name=).*", "\\1" + name, text, count=1) + text = re.sub(r"(GtkTheme=).*", "\\1" + name, text, count=1) + text = re.sub(r"(MetacityTheme=).*", "\\1" + name, text, count=1) + text = re.sub(r"(IconTheme=).*", "\\1" + name, text, count=1) + + text = re.sub(r"(Comment=).*", "\\1" + "A variant of " + os.path.basename(src_path) + " created by nicklasvraa/color-manager", text, count=1) + + with open(index_path, 'w') as file: + file.write(text) + +def copy_pack(src_path:str, dest_path:str, name:str) -> str: + """ Copy pack and return the resulting copy's directory path. """ + + src_path = expand_path(src_path) + dest_path = os.path.join(expand_path(dest_path), name) + + copy_file_structure(src_path, dest_path) + rename_pack(src_path, dest_path, name) + + return dest_path + +# Vector-based recoloring ------------------------------------------------------ + +def apply_monotones_to_vec(text:str, colors:Set[str], hsl:Tuple[float,float,float]) -> str: + """ Replace every instance of color within the given list with their monochrome equivalent in the given string representing an svg-file, determined by the given hue, saturation and lightness offset. """ + + h, s, _ = hsl + + if s == 0: + for color in colors: + graytone = hex_to_gray(color) + text = re.sub(color, graytone, text) + else: + for color in colors: + _, __, old_l = hex_to_hsl(color) + new_color = rgb_to_hex(hsl_to_rgb((h, s, old_l))) + text = re.sub(color, new_color, text) + + return text + +def apply_palette_to_vec(text:str, colors:Set[str], new_colors:Dict[str,Lab]) -> str: + """ Replace hexadecimal color codes in a given svg/xml/css string with their closest matches within the given color palette. """ + + for color in colors: + new_color = closest_match(color, new_colors) + h, s, _ = hex_to_hsl(new_color) + _, __, old_l = hex_to_hsl(color) + new_color = rgb_to_hex(hsl_to_rgb((h, s, old_l))) + text = re.sub(color, new_color, text) + + return text + +# Pixel-based recoloring ------------------------------------------------------- + +def apply_monotones_to_img(img:Image, hsl:Tuple[float,float,float]) -> Image: + """ Replace every instance of color within the given list with their monochrome equivalent in the given image, determined by the given hue, saturation and lightness offset. """ + + mode = img.mode + h, s, _ = hsl + + if s == 0: + if mode == "RGBA": img = img.convert("LA") + else: img = img.convert("L") + else: + width, height = img.size + + for x in range(width): + for y in range(height): + if mode == "RGBA": + r, g, b, a = img.getpixel((x, y)) + else: + r, g, b = img.getpixel((x, y)) + + _, __, old_l = rgb_to_hsl((r, g, b)) + new_color = hsl_to_rgb((h, s, old_l)) + + if mode == "RGBA": + img.putpixel((x,y), new_color + (a,)) + else: + img.putpixel((x,y), new_color) + + return img + +def apply_palette_to_img(img:Image, new_colors:Dict[str,Lab], args) -> Image: + """ Replace colors in a given image with the closest match within a given color palette. """ + + if args.smooth: img = img.convert("P", palette=Image.ADAPTIVE, colors=256) + else: img = img.convert("P") + + palette = img.getpalette() + + rgb_palette = [(palette[i], palette[i+1], palette[i+2]) for i in range(0, len(palette), 3)] + + hex_palette = ["#%02x%02x%02x" % rgb for rgb in rgb_palette] + + new_palette = [] + for color in hex_palette: + new_color = closest_match(color, new_colors) + h, s, _ = hex_to_hsl(new_color) + _, __, old_l = hex_to_hsl(color) + new_color = hsl_to_rgb((h, s, old_l)) + new_palette.extend(new_color) + + img.putpalette(new_palette) + return img + +# User interface functions ----------------------------------------------------- + +def recolor_modified(op:str, args) -> None: + """ Recursively copies and converts a source folder into a destination, given either an hsl color, a palette, or a color mapping. """ + + check_path(args.src) + + if op == "palette": + args.palette = generate_palette_dict(args.palette) + + # Recolor vector graphics. + paths = get_paths(args.src, [".svg", ".xml"]) + for path in tqdm(paths, desc="svg", disable=is_empty(paths)): + with open(path, 'r') as file: x = file.read() + + x = css_to_hex(x) + x = expand_all_hex(x) + colors = get_file_colors(x) + + if op == "monochrome": + x = apply_monotones_to_vec(x, colors, random.choice(args.monochrome)) + elif op == "palette": + x = apply_palette_to_vec(x, colors, args.palette) + + with open(path, 'w') as file: file.write(x) + + # Recolor stylesheets. + paths = get_paths(args.src, [".css", "rc"]) + for path in tqdm(paths, desc="css", disable=is_empty(paths)): + with open(path, 'r') as file: x = file.read() + + x = css_to_hex(x) + x = expand_all_hex(x) + colors = get_file_colors(x) + + if op == "monochrome": + x = apply_monotones_to_vec(x, colors, random.choice(args.monochrome)) + elif op == "palette": + x = apply_palette_to_vec(x, colors, args.palette) + + x = hex_to_css(x) + with open(path, 'w') as file: file.write(x) + + # Recolor pngs. + paths = get_paths(args.src, [".png"]) + for path in tqdm(paths, desc="png", disable=is_empty(paths)): + x = Image.open(path) + x = x.convert("RGBA") + a = x.split()[3] # Save original alpha channel. + + if op == "monochrome": + x = apply_monotones_to_img(x, random.choice(args.monochrome)) + elif op == "palette": + x = apply_palette_to_img(x, args.palette, args) + + x = x.convert("RGBA") + r,g,b,_ = x.split() + x = Image.merge("RGBA",(r,g,b,a)) # Restore original alpha channel. + x.save(path) + + # Recolor jpgs. + paths = get_paths(args.src, [".jpg", ".jpeg"]) + for path in tqdm(paths, desc="jpg", disable=is_empty(paths)): + x = Image.open(path) + x = x.convert("RGB") + + if op == "monochrome": + x = apply_monotones_to_img(x, random.choice(args.monochrome)) + elif op == "palette": + x = apply_palette_to_img(x, args.palette, args) + + x = x.convert("RGB") + x.save(path) + +def list_of_strings(arg): + return arg.split(',') + +def main(): + parser = argparse.ArgumentParser(description='Recolor an image.') + + parser.add_argument('--src', type=str) + parser.add_argument('--monochrome', type=list_of_strings) + parser.add_argument('--palette', type=list_of_strings) + parser.add_argument('--smooth', type=bool, default=True) + + args = parser.parse_args() + + if args.monochrome != None: + for idx in range(len(args.monochrome)): + args.monochrome[idx] = hex_to_hsl(args.monochrome[idx]) + recolor_modified("monochrome", args) + elif args.palette != None: + recolor_modified("palette", args) + +if __name__ == '__main__': + main() diff --git a/lib/monitors.nix b/lib/monitors.nix new file mode 100644 index 0000000..f1cda7d --- /dev/null +++ b/lib/monitors.nix @@ -0,0 +1,72 @@ +{ lib, config, ... }: +let + monitor = lib.types.submodule { + options = { + isMain = lib.mkOption { + type = lib.types.bool; + description = "Whether the monitor is the main one"; + default = false; + }; + scale = lib.mkOption { + type = lib.types.float; + description = "The scale of the monitor"; + default = 1.0; + }; + mode = lib.mkOption { + type = lib.types.submodule { + options = { + width = lib.mkOption { + type = lib.types.int; + description = "The width of the monitor"; + }; + height = lib.mkOption { + type = lib.types.int; + description = "The height of the monitor"; + }; + refresh = lib.mkOption { + type = lib.types.float; + description = "The refresh rate of the monitor"; + }; + }; + }; + }; + position = lib.mkOption { + type = lib.types.submodule { + options = { + x = lib.mkOption { + type = lib.types.int; + description = "The x position of the monitor"; + }; + y = lib.mkOption { + type = lib.types.int; + description = "The y position of the monitor"; + }; + }; + }; + }; + rotation = lib.mkOption { + type = lib.types.int; + description = "The rotation of the monitor"; + }; + }; + }; +in + +{ + options.monitors = lib.mkOption { + type = lib.types.attrsOf monitor; + }; + options.mainMonitorName = lib.mkOption { + type = lib.types.str; + }; + options.otherMonitorsNames = lib.mkOption { + type = lib.types.listOf lib.types.str; + }; + + config.mainMonitorName = + builtins.attrNames config.monitors + |> builtins.filter (name: config.monitors.${name}.isMain) + |> builtins.head; + config.otherMonitorsNames = + builtins.attrNames config.monitors |> builtins.filter (name: !config.monitors.${name}.isMain); +} diff --git a/lib/wallpaper/buildWallpaper.nix b/lib/wallpaper/buildWallpaper.nix new file mode 100644 index 0000000..1ba4c4f --- /dev/null +++ b/lib/wallpaper/buildWallpaper.nix @@ -0,0 +1,116 @@ +{ + pkgs, + lib, + config, + ... +}: +with builtins; +let + getWallpaper = + wallpaper: + let + wallpaperPkg = pkgs.fetchurl { inherit (wallpaper) name url sha256; }; + in + { + inherit (wallpaper) name convertMethod; + path = "${wallpaperPkg}"; + }; + + wallpapers = map getWallpaper config.wallpapers; + + goNord = wallpaper: import ./goNord.nix { inherit pkgs config wallpaper; }; + + lutgen = wallpaper: import ./lutgen.nix { inherit pkgs config wallpaper; }; + + getName = + path: + baseNameOf path |> match "(.*)\\..*" |> head |> lib.splitString "-" |> tail |> concatStringsSep "-"; + + generateWallpaper = + wallpaper: + let + inherit (wallpaper) path convertMethod; + name = getName path; + live = (toString path |> match ".*gif$") != null; + thisWallpaper = { inherit name path live; }; + in + { + inherit name live; + path = + if lib.strings.hasPrefix name config.lib.stylix.colors.scheme then + path + else if convertMethod == "gonord" then + goNord thisWallpaper + else if convertMethod == "lutgen" then + lutgen thisWallpaper + else + path; + }; + + generatedWallpapers = map generateWallpaper wallpapers; + + setWallpaper = + wallpaper: + let + inherit (wallpaper) name live path; + ext = if live then ".gif" else ".jpg"; + in + { + name = "Pictures/Wallpapers/generated/${name}${ext}"; + value = { + source = path; + }; + }; + + blurWallpaper = + wallpaper: + let + inherit (wallpaper) name path live; + in + if live then + null + else + { + name = "Pictures/Wallpapers/generated/${name}-blurred.jpg"; + value = { + source = pkgs.runCommand "${name}-blurred.jpg" { } '' + ${pkgs.imagemagick}/bin/magick ${path} -blur 0x20 $out + ''; + }; + }; + + normalWallpapers = map setWallpaper generatedWallpapers |> listToAttrs; + blurredWallpapers = map blurWallpaper generatedWallpapers |> listToAttrs; + + wallpaper = lib.types.submodule { + options = { + name = lib.mkOption { + type = lib.types.str; + description = "Name of the wallpaper"; + }; + url = lib.mkOption { + type = lib.types.str; + description = "URL of the wallpaper"; + }; + sha256 = lib.mkOption { + type = lib.types.str; + description = "SHA256 of the wallpaper"; + }; + convertMethod = lib.mkOption { + type = lib.types.str; + description = "Method to convert the wallpaper (gonord, lutgen, none)"; + default = "lutgen"; + }; + }; + }; +in +{ + options.wallpapers = lib.mkOption { + type = lib.types.listOf wallpaper; + description = "List of wallpapers"; + }; + + config = { + home.file = normalWallpapers // blurredWallpapers; + }; +} diff --git a/lib/wallpaper/goNord.nix b/lib/wallpaper/goNord.nix new file mode 100644 index 0000000..e12b54c --- /dev/null +++ b/lib/wallpaper/goNord.nix @@ -0,0 +1,50 @@ +{ + pkgs, + config, + wallpaper, +}: +let + goNordScript = + pkgs.writers.writePython3Bin "goNord" + { + libraries = with pkgs.python3Packages; [ + image-go-nord + pyyaml + ]; + doCheck = false; + } + '' + from ImageGoNord import GoNord + import argparse + import yaml + + parser = argparse.ArgumentParser(description="Go nord") + parser.add_argument( + "--colorscheme", "-c", help="Path to the yaml file containing the colorscheme" + ) + parser.add_argument("--image", "-i", help="Path to the image to quantize") + parser.add_argument( + "--output", "-o", help="Path to the output image", default="output.png" + ) + args = parser.parse_args() + colorscheme = args.colorscheme + image = args.image + output = args.output + + go_nord = GoNord() + go_nord.enable_avg_algorithm() + go_nord.enable_gaussian_blur() + image = go_nord.open_image(image) + if colorscheme: + go_nord.reset_palette() + palette = set(yaml.safe_load(open(colorscheme))["palette"].values()) + for color in palette: + go_nord.add_color_to_palette(color) + go_nord.quantize_image(image, save_path=output) + ''; + + inherit (wallpaper) name path; +in +pkgs.runCommand "${name}.jpg" { } '' + ${goNordScript}/bin/goNord -c ${config.stylix.base16Scheme} -i ${path} -o $out +'' diff --git a/lib/wallpaper/lutgen.nix b/lib/wallpaper/lutgen.nix new file mode 100644 index 0000000..cec3005 --- /dev/null +++ b/lib/wallpaper/lutgen.nix @@ -0,0 +1,18 @@ +{ + pkgs, + config, + wallpaper, +}: +let + inherit (wallpaper) name path live; + colors = builtins.concatStringsSep " " config.lib.stylix.colors.toList; +in +if live then + pkgs.runCommand "${name}.gif" { } '' + ${pkgs.lutgen}/bin/lutgen generate -o palette.png -- ${colors} + ${pkgs.ffmpeg}/bin/ffmpeg -i ${path} -i palette.png -filter_complex '[0][1] haldclut' $out + '' +else + pkgs.runCommand "${name}.jpg" { } '' + ${pkgs.lutgen}/bin/lutgen apply -o $out ${path} -- ${colors} + '' diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 0000000..8ced9ba --- /dev/null +++ b/nix/default.nix @@ -0,0 +1,17 @@ +{ nixpkgs, ... }: +{ + imports = [ + ./substituters.nix + ./nh.nix + ./nixpkgs.nix + ]; + + nix = { + settings.experimental-features = [ + "nix-command" + "flakes" + "pipe-operators" + ]; + nixPath = [ "nixpkgs=${nixpkgs}" ]; + }; +} diff --git a/nix/nh.nix b/nix/nh.nix new file mode 100644 index 0000000..d453feb --- /dev/null +++ b/nix/nh.nix @@ -0,0 +1,25 @@ +{ + self, + host, + user, + pkgs, + inputs, + ... +}: +{ + programs.nh = { + enable = true; + package = inputs.nh.packages.${pkgs.system}.nh; + clean = { + enable = true; + dates = "3 days"; + extraArgs = + let + numColorschemes = builtins.length self.homeConfigurations."${user}@${host}".config.colorSchemes; + numToKeep = numColorschemes * 2 |> toString; + in + "--keep ${numToKeep}"; + }; + }; + environment.variables.NH_FLAKE = "/home/${user}/.config/nixos"; +} diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix new file mode 100644 index 0000000..ee96f65 --- /dev/null +++ b/nix/nixpkgs.nix @@ -0,0 +1,10 @@ +{ + nixpkgs.config = { + allowUnfree = true; + permittedInsecurePackages = [ + "openssl-1.1.1w" + "electron-19.1.9" + ]; + allowUnsupportedSystem = true; + }; +} diff --git a/nix/substituters.nix b/nix/substituters.nix new file mode 100644 index 0000000..fca9bc2 --- /dev/null +++ b/nix/substituters.nix @@ -0,0 +1,23 @@ +{ user, ... }: +{ + nix.settings = { + trusted-users = [ user ]; + substituters = [ + "https://mirrors.tuna.tsinghua.edu.cn/nix-channels/store" + "https://mirrors.ustc.edu.cn/nix-channels/store" + "https://cache.nixos.org" + "https://nix-community.cachix.org" + "https://hyprland.cachix.org" + "https://nixpkgs-wayland.cachix.org" + "https://niri.cachix.org" + ]; + + trusted-public-keys = [ + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + "hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc=" + "nixpkgs-wayland.cachix.org-1:3lwxaILxMRkVhehr5StQprHdEo4IrE8sRho9R9HOLYA=" + "niri.cachix.org-1:Wv0OmO7PsuocRKzfDoJ3mulSl7Z6oezYhGhR+3W2964=" + ]; + }; +} diff --git a/os/default.nix b/os/default.nix new file mode 100644 index 0000000..068fcb9 --- /dev/null +++ b/os/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./system + ./programs + ]; +} diff --git a/os/programs/basic.nix b/os/programs/basic.nix new file mode 100644 index 0000000..f8b0af6 --- /dev/null +++ b/os/programs/basic.nix @@ -0,0 +1,24 @@ +{ + programs = { + fish.enable = true; + adb.enable = true; + light.enable = true; + wshowkeys.enable = true; + nix-ld.enable = true; + virt-manager.enable = true; + git = { + enable = true; + config = { + http = { + proxy = "http://127.0.0.1:7890"; + }; + https = { + proxy = "http://127.0.0.1:7890"; + }; + safe = { + directory = "*"; + }; + }; + }; + }; +} diff --git a/os/programs/default.nix b/os/programs/default.nix new file mode 100644 index 0000000..680121c --- /dev/null +++ b/os/programs/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./basic.nix + ./doas.nix + ./niri.nix + ./ollama.nix + ]; +} diff --git a/os/programs/doas.nix b/os/programs/doas.nix new file mode 100644 index 0000000..46d49f9 --- /dev/null +++ b/os/programs/doas.nix @@ -0,0 +1,16 @@ +{ user, ... }: +{ + security = { + doas = { + enable = true; + extraRules = [ + { + users = [ user ]; + noPass = true; + keepEnv = true; + } + ]; + }; + sudo.enable = true; + }; +} diff --git a/os/programs/niri.nix b/os/programs/niri.nix new file mode 100644 index 0000000..91602c1 --- /dev/null +++ b/os/programs/niri.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: +{ + programs.niri = { + enable = true; + package = pkgs.niri-unstable; + }; +} diff --git a/os/programs/ollama.nix b/os/programs/ollama.nix new file mode 100644 index 0000000..90905e3 --- /dev/null +++ b/os/programs/ollama.nix @@ -0,0 +1,8 @@ +{ + services.ollama = { + enable = true; + }; + services.nextjs-ollama-llm-ui = { + enable = true; + }; +} diff --git a/os/system/boot.nix b/os/system/boot.nix new file mode 100644 index 0000000..0bb5e5a --- /dev/null +++ b/os/system/boot.nix @@ -0,0 +1,36 @@ +{ pkgs, ... }: +{ + boot = { + kernelPackages = pkgs.linuxPackages_zen; + kernelParams = [ + "loglevel=3" + "quiet" + "spash" + "console=tty1" + ]; + consoleLogLevel = 0; + initrd.verbose = false; + loader = { + systemd-boot.enable = false; + grub = { + enable = true; + device = "nodev"; + useOSProber = true; + efiSupport = true; + efiInstallAsRemovable = true; + extraEntriesBeforeNixOS = true; + extraEntries = '' + menuentry "Reboot" { + reboot + } + menuentry "Poweroff" { + halt + } + ''; + default = "saved"; + }; + efi.efiSysMountPoint = "/boot"; + }; + plymouth.enable = true; + }; +} diff --git a/os/system/configuration.nix b/os/system/configuration.nix new file mode 100644 index 0000000..f374882 --- /dev/null +++ b/os/system/configuration.nix @@ -0,0 +1,219 @@ +{ + pkgs, + config, + user, + ... +}: +{ + imports = [ + ./boot.nix + ]; + + networking = { + networkmanager.enable = true; + proxy = { + default = "http://127.0.0.1:7890"; + httpProxy = "http://127.0.0.1:7890"; + httpsProxy = "http://127.0.0.1:7890"; + noProxy = "localhost,internal.domain"; + }; + }; + + time = { + timeZone = "Asia/Shanghai"; + hardwareClockInLocalTime = true; + }; + + i18n = { + extraLocaleSettings = { + LANGUAGE = "en_US.UTF-8"; + LC_ALL = "en_US.UTF-8"; + LC_ADDRESS = "en_US.UTF-8"; + LC_IDENTIFICATION = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; + }; + + inputMethod = { + enable = true; + type = "fcitx5"; + fcitx5.addons = with pkgs; [ + fcitx5-mozc + fcitx5-gtk + fcitx5-chinese-addons + fcitx5-rime + ]; + fcitx5.waylandFrontend = true; + }; + }; + + services = { + xserver = { + enable = true; + + displayManager.gdm.enable = false; + desktopManager.gnome.enable = true; + desktopManager.runXdgAutostartIfNone = true; + + xkb.layout = "us"; + xkb.variant = ""; + }; + + displayManager.sessionPackages = [ pkgs.hyprland ]; + + greetd = { + enable = true; + vt = 3; + settings.default_session = { + command = # bash + let + inherit (config.services.displayManager.sessionData) desktops; + in + # bash + '' + ${pkgs.greetd.tuigreet}/bin/tuigreet --time \ + --sessions ${desktops}/share/xsessions:${desktops}/share/wayland-sessions \ + --remember --remember-user-session --asterisks --cmd niri-session \ + --user-menu --greeting "Who TF Are You?" --window-padding 2''; + user = "greeter"; + }; + }; + + fprintd = { + enable = true; + }; + + printing.enable = true; + + flatpak.enable = true; + + pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + }; + + blueman.enable = true; + + gnome.gnome-browser-connector.enable = true; + + gvfs.enable = true; + }; + + security = { + rtkit.enable = true; + sudo.extraRules = [ + { + users = [ user ]; + commands = [ + { + command = "ALL"; + options = [ "NOPASSWD" ]; + } + ]; + } + ]; + + polkit.enable = true; + }; + + hardware.bluetooth.enable = true; + hardware.bluetooth.powerOnBoot = true; + + documentation.man.generateCaches = true; + + users.users.${user} = { + shell = pkgs.fish; + isNormalUser = true; + description = "Eden Lee"; + extraGroups = [ + "networkmanager" + "wheel" + "adbuser" + "docker" + "libvirtd" + "video" + ]; + packages = with pkgs; [ + nautilus + loupe + podman-compose + ]; + }; + + environment = { + systemPackages = with pkgs; [ + git + gcc + wget + curl + gnumake + cmake + distrobox + ntfs3g + base16-schemes + home-manager + polkit + polkit_gnome + ]; + + variables = { + EDITOR = "lvim"; + GDK_SCALE = ""; + GDK_DPI_SCALE = ""; + NIRI_CONFIG = "/home/${user}/.config/niri/config-override.kdl"; + }; + + sessionVariables = { + XMODIFIERS = "@im=fcitx"; + SDL_IM_MODULE = "fcitx"; + GLFW_IM_MODULE = "ibus"; + QT_SCALE_FACTOR_ROUNDING_POLICY = "round"; + GSK_RENDERER = "vulkan"; + NIXOS_OZONE_WL = "1"; + }; + + localBinInPath = true; + }; + + systemd.user.services = { + polkit-gnome-authentication-agent-1 = { + description = "polkit-gnome-authentication-agent-1"; + wantedBy = [ "graphical-session.target" ]; + wants = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; + Restart = "on-failure"; + RestartSec = 1; + TimeoutStopSec = 10; + }; + }; + niri-flake-polkit.enable = false; + }; + + virtualisation = { + libvirtd.enable = true; + + podman = { + enable = true; + defaultNetwork.settings.dns_enabled = true; + }; + + docker = { + enable = true; + storageDriver = "btrfs"; + rootless = { + enable = true; + setSocketVariable = true; + }; + }; + }; +} diff --git a/os/system/default.nix b/os/system/default.nix new file mode 100644 index 0000000..4c95932 --- /dev/null +++ b/os/system/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./configuration.nix + ./boot.nix + ./fonts.nix + ./stylix.nix + ]; +} diff --git a/os/system/fonts.nix b/os/system/fonts.nix new file mode 100644 index 0000000..c538c8c --- /dev/null +++ b/os/system/fonts.nix @@ -0,0 +1,17 @@ +{ pkgs, ... }: +{ + fonts.packages = with pkgs; [ + noto-fonts + noto-fonts-cjk-sans + noto-fonts-cjk-serif + noto-fonts-color-emoji + noto-fonts-emoji + nerd-fonts.symbols-only + nerd-fonts.droid-sans-mono + nerd-fonts.monofur + font-awesome + lxgw-wenkai + nasin-nanpa + ]; + fonts.fontDir.enable = true; +} diff --git a/os/system/stylix.nix b/os/system/stylix.nix new file mode 100644 index 0000000..3412585 --- /dev/null +++ b/os/system/stylix.nix @@ -0,0 +1,29 @@ +{ + self, + lib, + host, + user, + ... +}: +let + cfg = self.homeConfigurations."${user}@${host}".config; +in +{ + stylix = { + enable = true; + base16Scheme = lib.mkDefault cfg.stylix.base16Scheme; + autoEnable = false; + targets = { + console.enable = true; + gnome.enable = true; + grub.enable = true; + plymouth.enable = true; + }; + }; + specialisation = builtins.mapAttrs (name: value: { + configuration = { + stylix.base16Scheme = lib.mkForce cfg.specialisation.${name}.configuration.stylix.base16Scheme; + environment.etc."specialisation".text = name; + }; + }) cfg.specialisation; +} diff --git a/overlays/customColorSchemes/colorSchemes/paradise.yaml b/overlays/customColorSchemes/colorSchemes/paradise.yaml new file mode 100644 index 0000000..e1cc343 --- /dev/null +++ b/overlays/customColorSchemes/colorSchemes/paradise.yaml @@ -0,0 +1,21 @@ +system: "base16" +name: "paradise" +author: "https://github.com/joshuah345" +variant: "dark" +palette: + base00: "#0a0a0a" + base01: "#151515" + base02: "#424242" + base03: "#727272" + base04: "#727272" + base05: "#e8e3e3" + base06: "#a988b0" + base07: "#a988b0" + base08: "#b66467" + base09: "#d9bc8c" + base0A: "#d9bc8c" + base0B: "#8c977d" + base0C: "#8aa6a2" + base0D: "#8da3b9" + base0E: "#a988b0" + base0F: "#e2d8e2" diff --git a/overlays/customColorSchemes/default.nix b/overlays/customColorSchemes/default.nix new file mode 100644 index 0000000..06d4f53 --- /dev/null +++ b/overlays/customColorSchemes/default.nix @@ -0,0 +1,28 @@ +{ pkgs, ... }: +let + customColorSchemes = pkgs.stdenvNoCC.mkDerivation { + name = "customColorSchemes"; + src = ./colorSchemes; + installPhase = '' + mkdir -p $out/share/themes + cp *.yaml $out/share/themes + ''; + }; +in +{ + nixpkgs.overlays = [ + (final: prev: { + base16-schemes = prev.base16-schemes.overrideAttrs (oldAttrs: { + installPhase = '' + runHook preInstall + + mkdir -p $out/share/themes/ + install base16/*.yaml $out/share/themes/ + install ${customColorSchemes}/share/themes/*.yaml $out/share/themes/ + + runHook postInstall + ''; + }); + }) + ]; +} diff --git a/overlays/default.nix b/overlays/default.nix new file mode 100644 index 0000000..be91eee --- /dev/null +++ b/overlays/default.nix @@ -0,0 +1,10 @@ +{ inputs, ... }: +{ + imports = [ + ./qutebrowser.nix + ./customColorSchemes + ]; + nixpkgs.overlays = [ + inputs.niri.overlays.niri + ]; +} diff --git a/overlays/qutebrowser.nix b/overlays/qutebrowser.nix new file mode 100644 index 0000000..b8ba9ea --- /dev/null +++ b/overlays/qutebrowser.nix @@ -0,0 +1,5 @@ +{ + nixpkgs.overlays = [ + (_: prev: { qutebrowser = prev.qutebrowser.override { enableWideVine = true; }; }) + ]; +} diff --git a/pkgs/edenfetch.nix b/pkgs/edenfetch.nix new file mode 100644 index 0000000..4d34150 --- /dev/null +++ b/pkgs/edenfetch.nix @@ -0,0 +1,23 @@ +{ + lib, + fetchFromGitHub, + rustPlatform, +}: +rustPlatform.buildRustPackage { + pname = "edenfetch"; + version = "0.1.0"; + src = fetchFromGitHub { + owner = "EdenQwQ"; + repo = "edenfetch"; + rev = "ae6cc1207990e35288ed4a58caa21564bf1df9c3"; + hash = "sha256-ipdz6tIB7BF43lwk+KN+9orW0FLt7HJsCZuN2wbe6Is="; + }; + useFetchCargoVendor = true; + cargoHash = "sha256-t+tHvhTMwQwmozit6vcqE9iRZElaG4wJdzg79gcBJNY="; + meta = { + description = "edenfetch is a minimal fetch program written in Rust."; + homepage = "https://github.com/EdenQwQ/edenfetch"; + mainProgram = "edenfetch"; + maintainers = with lib.maintainers; [ EdenQwQ ]; + }; +} diff --git a/pkgs/fonts/hugmetight.nix b/pkgs/fonts/hugmetight.nix new file mode 100644 index 0000000..9b82878 --- /dev/null +++ b/pkgs/fonts/hugmetight.nix @@ -0,0 +1,30 @@ +{ + lib, + stdenvNoCC, + pkgs, +}: +stdenvNoCC.mkDerivation { + pname = "kose-font"; + version = "2025-03-01"; + + src = pkgs.fetchzip { + url = "https://mistifonts.com/fonts/hug-me-tight.zip"; + sha256 = "sha256-GLcAKu9AeYZ8j285ykiwx31Qoja6tZCXQR88CTu27UY="; + stripRoot = false; + }; + + installPhase = '' + runHook preInstall + + mkdir -p $out/share/fonts + cp *.ttf $out/share/fonts + + runHook postInstall + ''; + + meta = with lib; { + description = "The Hug Me Tight font"; + maintainers = with maintainers; [ EdenQwQ ]; + platforms = platforms.all; + }; +} diff --git a/pkgs/fonts/kose.nix b/pkgs/fonts/kose.nix new file mode 100644 index 0000000..41839f1 --- /dev/null +++ b/pkgs/fonts/kose.nix @@ -0,0 +1,46 @@ +{ + lib, + stdenvNoCC, + pkgs, +}: +let + mono = pkgs.fetchurl { + name = "XiaolaiMonoSC-Regular.ttf"; + url = "https://github.com/lxgw/kose-font/releases/download/v3.120/XiaolaiMonoSC-Regular.ttf"; + sha256 = "1wxjjvd6dr9va6i65rl5nsjmxmzpm7xz4pnr0afs9n4v6vgvpq51"; + }; + regular = pkgs.fetchurl { + name = "XiaolaiSC-Regular"; + url = "https://github.com/lxgw/kose-font/releases/download/v3.120/XiaolaiSC-Regular.ttf"; + sha256 = "0zr043nj3prw5v4znarhzi1jigr7f6lxnajsrj10gzqaxf4wdq35"; + }; +in +stdenvNoCC.mkDerivation { + pname = "kose-font"; + version = "2025-02-22"; + + srcs = [ + mono + regular + ]; + + dontUnpack = true; + + installPhase = '' + runHook preInstall + + mkdir -p $out/share/fonts + cp ${mono} $out/share/fonts + cp ${regular} $out/share/fonts + + mkdir -p $out/etc/fonts/conf.d + + runHook postInstall + ''; + + meta = with lib; { + description = "The Kose font"; + maintainers = with maintainers; [ EdenQwQ ]; + platforms = platforms.all; + }; +} diff --git a/pkgs/zju-connect.nix b/pkgs/zju-connect.nix new file mode 100644 index 0000000..f19281c --- /dev/null +++ b/pkgs/zju-connect.nix @@ -0,0 +1,27 @@ +{ + lib, + buildGoModule, + fetchFromGitHub, + ... +}: +buildGoModule rec { + pname = "zju-connect"; + version = "0.8.0"; + + src = fetchFromGitHub { + owner = "Mythologyli"; + repo = "ZJU-Connect"; + rev = "v${version}"; + hash = "sha256-8QMdesmveXHmAKhuISmAE75La/KeybFqYSfAACfmIJE="; + }; + + vendorHash = "sha256-ANb3zcZCMqg6iO79q9CQEEN8DH0cwb7bAs3YmhfGTz8="; + + meta = { + description = "ZJU RVPN client"; + homepage = "https://github.com/Mythologyli/ZJU-Connect"; + mainProgram = "zju-connect"; + license = lib.licenses.agpl3Only; + maintainers = with lib.maintainers; [ EdenQwQ ]; + }; +} diff --git a/sharedConfig.nix b/sharedConfig.nix new file mode 100644 index 0000000..c6f5eff --- /dev/null +++ b/sharedConfig.nix @@ -0,0 +1,62 @@ +{ + colorSchemes = [ + { + name = "gruvbox-material-dark-soft"; + isDefault = true; + } + "everforest" + "nord" + { + name = "green-blue-flowers-dark"; + matugen = { + image = "green-blue-flowers.jpg"; + scheme = "scheme-expressive"; + }; + } + ]; + + wallpapers = [ + { + name = "mygo-watch-tv.png"; + url = "https://i.imgur.com/FSneBN2.jpg"; + sha256 = "1pznrjx6rb8qm947q63dzkrmv188h3nrcp8cdmd7hs5gs30azfww"; + convertMethod = "gonord"; + } + { + name = "frieren-butterflies.jpg"; + url = "https://i.imgur.com/H1noDhu.jpg"; + sha256 = "0vypn9sxarv2gw42hs2haasyvzqyp02s6vaqygp9xbg59m0x2l73"; + convertMethod = "lutgen"; + } + { + name = "frieren-fire.jpg"; + url = "https://i.imgur.com/c3CWmia.jpg"; + sha256 = "0lgqwjl6jd1y84cz368s4sq0krzg67znqxirzapqxqvfdpn9rwbw"; + convertMethod = "lutgen"; + } + { + name = "anon-soyo.jpg"; + url = "https://i.imgur.com/koPV5sz.png"; + sha256 = "031s3v8fp5q2dm95wra898jq9l4i49wlwf97iah6bjqi5ihaxs2x"; + convertMethod = "gonord"; + } + { + name = "mygo-train.jpg"; + url = "https://i.imgur.com/OzR8c12.jpg"; + sha256 = "0fnwasnr19wfyhxa5yq7g3315d1641602x7fg8sv1qv95dlj55w2"; + convertMethod = "gonord"; + } + { + name = "green-blue-flowers.jpg"; + url = "https://i.imgur.com/Kvjqksw.jpg"; + sha256 = "1la4g50sxc940j9vcf7440l73ycx05z63dmpfq8xn0b746hzmnkq"; + convertMethod = "gonord"; + } + { + name = "bangqiaoyan-girl-sky.jpg"; + url = "https://i.imgur.com/qJ3ta1b.jpg"; + sha256 = "1chzklk6j893fdxhp0jhjwgwyhg3p6hjrgrrr5fw8gkllx91sx5w"; + convertMethod = "gonord"; + } + ]; +}