aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetri Hienonen <petri.hienonen@gmail.com>2024-05-23 13:56:00 +0300
committerPetri Hienonen <petri.hienonen@gmail.com>2025-11-30 12:29:57 +0200
commit08297376a85a1719518507e54fca9de954d2376a (patch)
tree3b9c58304b40248533bbb2bb5b7bad2da9da1ff0
parent75c2af4aedd2ac5c2cfc74b346625fa4b265541d (diff)
downloadnixos-08297376a85a1719518507e54fca9de954d2376a.tar.zst
Agenix configuration
-rwxr-xr-x.githooks/pre-commit1
-rw-r--r--.gitignore1
-rw-r--r--.luarc.json358
-rwxr-xr-xMakefile26
-rw-r--r--README.adoc17
-rw-r--r--flake.lock135
-rw-r--r--flake.nix186
-rw-r--r--home/alacritty/default.nix71
-rw-r--r--home/cargo/default.nix13
-rw-r--r--home/chawan/default.nix22
-rw-r--r--home/conky/default.nix76
-rw-r--r--home/conky/main.lua45
-rw-r--r--home/default.nix776
-rw-r--r--home/dunst/default.nix157
-rw-r--r--home/eww/default.nix197
-rw-r--r--home/firefox/default.nix164
-rw-r--r--home/fish/default.nix110
-rw-r--r--home/git/default.nix22
-rw-r--r--home/helix/default.nix151
-rw-r--r--home/hyprland/default.nix114
-rw-r--r--home/hyprland/hyprpaper.conf2
-rw-r--r--home/hyprlock/default.nix72
-rw-r--r--home/icons377
-rw-r--r--home/lf/default.nix41
-rw-r--r--home/lock/default.nix62
-rw-r--r--home/mail/default.nix244
-rw-r--r--home/mpv/default.nix25
-rw-r--r--home/newsboat/default.nix59
-rw-r--r--home/nvim/autocommands.lua272
-rw-r--r--home/nvim/default.nix93
-rw-r--r--home/nvim/keymaps.lua86
-rw-r--r--home/nvim/lsp.lua305
-rw-r--r--home/nvim/options.lua93
-rw-r--r--home/nvim/plugins/dap.lua147
-rw-r--r--home/nvim/plugins/mini.lua14
-rw-r--r--home/nvim/plugins/other.lua5
-rw-r--r--home/nvim/plugins/treesitter.lua81
-rw-r--r--home/nvim/plugins/undotree.lua1
-rw-r--r--home/nvim/plugins/which.lua6
-rw-r--r--home/quickshell/PopupContext.qml6
-rw-r--r--home/quickshell/README.md13
-rw-r--r--home/quickshell/Theme.qml87
-rw-r--r--home/quickshell/bar/Bar.qml115
-rw-r--r--home/quickshell/bar/BarBlock.qml75
-rw-r--r--home/quickshell/bar/BarText.qml57
-rw-r--r--home/quickshell/bar/Notification.qml11
-rw-r--r--home/quickshell/bar/NotificationPanel.qml101
-rw-r--r--home/quickshell/bar/Tooltip.qml89
-rw-r--r--home/quickshell/bar/blocks/ActiveWorkspace.qml34
-rw-r--r--home/quickshell/bar/blocks/Battery.qml50
-rw-r--r--home/quickshell/bar/blocks/Date.qml10
-rw-r--r--home/quickshell/bar/blocks/Datetime.qml31
-rw-r--r--home/quickshell/bar/blocks/Icon.qml146
-rw-r--r--home/quickshell/bar/blocks/Memory.qml31
-rw-r--r--home/quickshell/bar/blocks/Notifications.qml34
-rw-r--r--home/quickshell/bar/blocks/Sound.qml176
-rw-r--r--home/quickshell/bar/blocks/SystemTray.qml80
-rw-r--r--home/quickshell/bar/blocks/Time.qml10
-rw-r--r--home/quickshell/bar/blocks/Workspace.qml28
-rw-r--r--home/quickshell/bar/blocks/Workspaces.qml74
-rw-r--r--home/quickshell/bar/utils/HyprlandUtils.qml54
-rw-r--r--home/quickshell/default.nix20
-rw-r--r--home/quickshell/quickshell.nix90
-rw-r--r--home/quickshell/shell.qml8
-rw-r--r--home/tenere/default.nix16
-rw-r--r--home/ticker/default.nix41
-rw-r--r--home/vale/default.nix31
-rw-r--r--home/walker/config.toml251
-rw-r--r--home/walker/default.nix9
-rw-r--r--home/wallpapers/crow.jpgbin0 -> 985573 bytes
-rw-r--r--home/wallpapers/crow2.avifbin0 -> 91595 bytes
-rw-r--r--home/wallpapers/default.nix87
-rw-r--r--home/wallpapers/owl1.webpbin0 -> 161844 bytes
-rw-r--r--home/wallpapers/owl2.webpbin0 -> 1000782 bytes
-rw-r--r--home/wallpapers/owl3.webpbin0 -> 514680 bytes
-rw-r--r--home/waybar/config160
-rw-r--r--home/waybar/default.nix304
-rw-r--r--home/waybar/style.css354
-rw-r--r--hosts/kataja/default.nix141
-rw-r--r--hosts/kataja/hardware-configuration.nix79
-rw-r--r--hosts/pihlaja/default.nix269
-rw-r--r--hosts/pihlaja/hardware-configuration.nix80
-rw-r--r--hosts/saarni/default.nix316
-rw-r--r--hosts/saarni/hardware-configuration.nix81
-rw-r--r--hosts/tammi/20-wired.network31
-rw-r--r--hosts/tammi/21-wired.network48
-rw-r--r--hosts/tammi/CM3588.md284
-rw-r--r--hosts/tammi/Caddyfile319
-rw-r--r--hosts/tammi/cgitrc47
-rw-r--r--hosts/tammi/feeds.opml118
-rw-r--r--hosts/tammi/iptable-nat.service11
-rw-r--r--hosts/tammi/syntax-highlighting.js23
-rw-r--r--roles/shared.nix818
-rw-r--r--roles/wallpaper.nix23
-rw-r--r--secrets/duckdns_login_token.agebin0 -> 432 bytes
-rw-r--r--secrets/gmail.age9
-rw-r--r--secrets/miniflux_api_key.age9
-rw-r--r--secrets/openai_auth_token.agebin0 -> 600 bytes
-rw-r--r--secrets/relesoft.age9
-rw-r--r--secrets/relesoft_cargo_token.age9
-rw-r--r--secrets/s3fs.agebin0 -> 494 bytes
-rw-r--r--secrets/secrets.nix21
-rw-r--r--secrets/shiori_password.age10
-rw-r--r--secrets/unsplash_access_key.agebin0 -> 476 bytes
104 files changed, 9200 insertions, 865 deletions
diff --git a/.githooks/pre-commit b/.githooks/pre-commit
new file mode 100755
index 0000000..a78682d
--- /dev/null
+++ b/.githooks/pre-commit
@@ -0,0 +1 @@
+fd -e nix --search-path ../ -x nixfmt --strict --verify
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2863a3f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.plain
diff --git a/.luarc.json b/.luarc.json
new file mode 100644
index 0000000..985229b
--- /dev/null
+++ b/.luarc.json
@@ -0,0 +1,358 @@
+{
+ "format": {
+ "enable": true,
+ "defaultConfig": {
+ "indent_style": "tab",
+ "indent_size": "1",
+ "quote_style": "double",
+ "max_line_length": "120"
+ }
+ },
+ "workspace.library": [
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/alpha-nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/gitsigns.nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/gruvbox.nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/hardtime.nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/lualine.nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/nvim-dap",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/nvim-dap-view",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/nvim-dap-virtual-text",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/nvim-treesitter",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/nvim-web-devicons",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/telescope-fzf-native.nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/telescope.nvim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/undotree",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vim-fugitive",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ada",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-agda",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-angular",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-apex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-arduino",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-asm",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-astro",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-authzed",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-awk",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-bash",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-bass",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-beancount",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-bibtex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-bicep",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-bitbake",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-blade",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-blueprint",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-bp",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-brightscript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-c",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-c3",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-c_sharp",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-caddy",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cairo",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-capnp",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-chatito",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-circom",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-clojure",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cmake",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-comment",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-commonlisp",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cooklang",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-corn",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cpon",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cpp",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-css",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-csv",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cuda",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cue",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-cylc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-d",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-dart",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-desktop",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-devicetree",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-dhall",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-diff",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-disassembly",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-djot",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-dockerfile",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-dot",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-doxygen",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-dtd",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-earthfile",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ebnf",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-editorconfig",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-eds",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-eex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-elixir",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-elm",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-elsa",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-elvish",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-embedded_template",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-enforce",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-erlang",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-facility",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-faust",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fennel",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fidl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-firrtl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fish",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-foam",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-forth",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fortran",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fsh",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fsharp",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-func",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-fusion",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gap",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gaptst",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gdscript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gdshader",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-git_config",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-git_rebase",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gitattributes",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gitcommit",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gitignore",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gleam",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-glimmer",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-glimmer_javascript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-glimmer_typescript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-glsl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gn",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gnuplot",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-go",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-goctl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-godot_resource",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gomod",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gosum",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gotmpl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gowork",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gpg",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-graphql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gren",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-groovy",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-groq",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-gstlaunch",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hack",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hare",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-haskell",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-haskell_persistent",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hcl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-heex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-helm",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hjson",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hlsl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hlsplaylist",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hocon",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hoon",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-html",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-htmldjango",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-http",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hurl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-hyprlang",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-idl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-idris",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ini",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-inko",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ispc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-janet_simple",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-java",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-javadoc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-javascript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-jinja",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-jinja_inline",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-jq",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-jsdoc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-json",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-json5",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-jsonc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-jsonnet",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-julia",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-just",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-kcl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-kconfig",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-kdl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-kotlin",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-koto",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-kusto",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-lalrpop",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-latex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ledger",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-leo",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-linkerscript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-liquid",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-liquidsoap",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-llvm",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-lua",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-luadoc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-luap",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-luau",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-m68k",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-make",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-markdown",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-markdown_inline",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-matlab",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-menhir",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-mermaid",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-meson",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-mlir",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-muttrc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nasm",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nginx",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nickel",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nim_format_string",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ninja",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nix",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nqc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-nu",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-objc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-objdump",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ocaml",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ocaml_interface",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ocamllex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-odin",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pascal",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-passwd",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pem",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-perl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-php",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-php_only",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-phpdoc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pioasm",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pkl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-po",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pod",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-poe_filter",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pony",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-powershell",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-printf",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-prisma",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-problog",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-prolog",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-promql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-properties",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-proto",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-prql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-psv",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pug",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-puppet",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-purescript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-pymanifest",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-python",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-qmldir",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-qmljs",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-query",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-r",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-racket",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ralph",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rasi",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-razor",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rbs",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-re2c",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-readline",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-regex",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rego",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-requirements",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rescript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rifleconf",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rnoweb",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-robot",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-robots",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-roc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ron",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rst",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ruby",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-runescript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-rust",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-scala",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-scfg",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-scheme",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-scss",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sflog",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-slang",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-slim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-slint",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-smali",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-smithy",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-snakemake",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-snl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-solidity",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-soql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sosl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sourcepawn",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sparql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sproto",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sql",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-squirrel",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ssh_config",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-starlark",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-strace",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-styled",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-supercollider",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-superhtml",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-surface",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-svelte",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sway",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-swift",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-sxhkdrc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-systemtap",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-systemverilog",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-t32",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tablegen",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tact",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tcl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-teal",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-templ",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tera",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-terraform",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-textproto",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-thrift",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tiger",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tlaplus",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tmux",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-todotxt",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-toml",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tsv",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-tsx",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-turtle",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-twig",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-typescript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-typespec",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-typoscript",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-typst",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-udev",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ungrammar",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-unison",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-usd",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-uxntal",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-v",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vala",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vento",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vhdl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vhs",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vim",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vimdoc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vrl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-vue",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-wgsl",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-wgsl_bevy",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-wing",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-wit",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-wxml",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-xcompose",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-xml",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-xresources",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-yaml",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-yang",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-yuck",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-zathurarc",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-zig",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ziggy",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/vimplugin-treesitter-grammar-ziggy_schema",
+ "/nix/store/4xnnfyvh125ykhgp83ybpjbgxq56gllv-vim-pack-dir/pack/myNeovimPackages/start/which-key.nvim",
+ "/home/petri/.config/nvim",
+ "/nix/store/jfyklnaiyw0f0ik0gi81y2j292i2iix8-ghostty-1.1.3/share/nvim/site",
+ "/nix/store/mvmm0i2jwvjzdbsd3la48f1d48y2k2m3-neovim-unwrapped-0.11.3/share/nvim/runtime",
+ "/nix/store/mvmm0i2jwvjzdbsd3la48f1d48y2k2m3-neovim-unwrapped-0.11.3/lib/nvim",
+ "${3rd}/luassert/library"
+ ]
+}
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..301b3ae
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+default: rebuild
+
+rebuild:
+ run0 nixos-rebuild switch --flake .
+
+update:
+ nix flake update
+ run0 nixos-rebuild switch --flake .
+
+format:
+ fd -e nix -x nixfmt --strict --verify
+
+clean:
+ run0 nix-env --delete-generations +2 --profile /nix/var/nix/profiles/system
+ run0 nix-store --gc
+ run0 nix-store --optimise
+
+validate:
+ run0 nix-store --repair --verify --check-contents
+ run0 nix-store --optimise
+
+index:
+ systemd-run --scope -p MemoryMax=20G --user nix-index
+
+firefoxaddons:
+ nix flake show "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..530e465
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,17 @@
+= Nixos
+
+- Petri's NixOS installation
+- Operation commands are listed in `Makefile`.
+
+
+== Agenix
+
+Agenix can be used directly with SSH public keys.
+
+Create an encrypted age file:
+
+ cat gmail.pass.plain | agenix -e gmail.age
+
+Validate that the secret can be extracted:
+
+ agenix -d relesoft.age -i /home/petri/.ssh/id_ed25519
diff --git a/flake.lock b/flake.lock
index 088625d..46fe11c 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,33 +1,121 @@
{
"nodes": {
+ "agenix": {
+ "inputs": {
+ "darwin": "darwin",
+ "home-manager": "home-manager",
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1762618334,
+ "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=",
+ "owner": "ryantm",
+ "repo": "agenix",
+ "rev": "fcdea223397448d35d9b31f798479227e80183f6",
+ "type": "github"
+ },
+ "original": {
+ "owner": "ryantm",
+ "repo": "agenix",
+ "type": "github"
+ }
+ },
+ "darwin": {
+ "inputs": {
+ "nixpkgs": [
+ "agenix",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1744478979,
+ "narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
+ "owner": "lnl7",
+ "repo": "nix-darwin",
+ "rev": "43975d782b418ebf4969e9ccba82466728c2851b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "lnl7",
+ "ref": "master",
+ "repo": "nix-darwin",
+ "type": "github"
+ }
+ },
+ "firefox-addons": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "dir": "pkgs/firefox-addons",
+ "lastModified": 1764389080,
+ "narHash": "sha256-BEn1Z9Uv20u2DS6wzLKdzx5kAzynM3wMQ9JnGf3VJvI=",
+ "owner": "rycee",
+ "repo": "nur-expressions",
+ "rev": "897437c09bf22ce59efb3370f0783d0c662dba31",
+ "type": "gitlab"
+ },
+ "original": {
+ "dir": "pkgs/firefox-addons",
+ "owner": "rycee",
+ "repo": "nur-expressions",
+ "type": "gitlab"
+ }
+ },
"home-manager": {
"inputs": {
"nixpkgs": [
+ "agenix",
"nixpkgs"
]
},
"locked": {
- "lastModified": 1715381426,
- "narHash": "sha256-wPuqrAQGdv3ISs74nJfGb+Yprm23U/rFpcHFFNWgM94=",
+ "lastModified": 1745494811,
+ "narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "ab5542e9dbd13d0100f8baae2bc2d68af901f4b4",
+ "rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
"type": "github"
},
"original": {
"owner": "nix-community",
- "ref": "release-23.11",
+ "repo": "home-manager",
+ "type": "github"
+ }
+ },
+ "home-manager_2": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1763992789,
+ "narHash": "sha256-WHkdBlw6oyxXIra/vQPYLtqY+3G8dUVZM8bEXk0t8x4=",
+ "owner": "nix-community",
+ "repo": "home-manager",
+ "rev": "44831a7eaba4360fb81f2acc5ea6de5fde90aaa3",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "release-25.05",
"repo": "home-manager",
"type": "github"
}
},
"nixos-hardware": {
"locked": {
- "lastModified": 1716173274,
- "narHash": "sha256-FC21Bn4m6ctajMjiUof30awPBH/7WjD0M5yqrWepZbY=",
+ "lastModified": 1764440730,
+ "narHash": "sha256-ZlJTNLUKQRANlLDomuRWLBCH5792x+6XUJ4YdFRjtO4=",
"owner": "NixOS",
"repo": "nixos-hardware",
- "rev": "d9e0b26202fd500cf3e79f73653cce7f7d541191",
+ "rev": "9154f4569b6cdfd3c595851a6ba51bfaa472d9f3",
"type": "github"
},
"original": {
@@ -37,27 +125,27 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1716218643,
- "narHash": "sha256-i/E7gzQybvcGAYDRGDl39WL6yVk30Je/NXypBz6/nmM=",
+ "lastModified": 1764316264,
+ "narHash": "sha256-82L+EJU+40+FIdeG4gmUlOF1jeSwlf2AwMarrpdHF6o=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "a8695cbd09a7ecf3376bd62c798b9864d20f86ee",
+ "rev": "9a7b80b6f82a71ea04270d7ba11b48855681c4b0",
"type": "github"
},
"original": {
"owner": "nixos",
- "ref": "nixos-23.11",
+ "ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
- "lastModified": 1716137900,
- "narHash": "sha256-sowPU+tLQv8GlqtVtsXioTKeaQvlMz/pefcdwg8MvfM=",
+ "lastModified": 1764242076,
+ "narHash": "sha256-sKoIWfnijJ0+9e4wRvIgm/HgE27bzwQxcEmo2J/gNpI=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "6c0b7a92c30122196a761b440ac0d46d3d9954f1",
+ "rev": "2fad6eac6077f03fe109c4d4eb171cf96791faa4",
"type": "github"
},
"original": {
@@ -69,11 +157,28 @@
},
"root": {
"inputs": {
- "home-manager": "home-manager",
+ "agenix": "agenix",
+ "firefox-addons": "firefox-addons",
+ "home-manager": "home-manager_2",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable"
}
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
}
},
"root": "root",
diff --git a/flake.nix b/flake.nix
index 44581ae..5ecc52e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,40 +1,188 @@
{
description = "Petri's system";
inputs = {
- nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-23.11";
+ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-25.05";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
home-manager = {
- url = "github:nix-community/home-manager/release-23.11";
+ url = "github:nix-community/home-manager/release-25.05";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ agenix = {
+ url = "github:ryantm/agenix";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ firefox-addons = {
+ url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
inputs.nixpkgs.follows = "nixpkgs";
};
};
- outputs = inputs@{ self, nixpkgs, nixpkgs-unstable, home-manager, nixos-hardware, ... }:
- let
- inherit (self) outputs;
- vars = {
- user = "petri";
- name = "Petri Hienonen";
- email= "petri.hienonen@gmail.com";
- location = "$HOME/.nix";
- terminal = "alacritty";
- editor = "helix";
- shell = "fish";
- };
- in {
+ outputs =
+ inputs@{
+ self,
+ nixpkgs,
+ agenix,
+ home-manager,
+ nixpkgs-unstable,
+ nixos-hardware,
+ ...
+ }:
+ let
+ inherit (self) outputs;
+ vars = {
+ user = "petri";
+ name = "Petri Hienonen";
+ email = "petri.hienonen@gmail.com";
+ location = "$HOME/.nix";
+ terminal = "ghostty";
+ editor = "nvim";
+ shell = "fish";
+ };
+ pkgs-unstable = import nixpkgs-unstable {
+ system = "x86_64-linux";
+ config.allowUnfree = true;
+ };
+ in
+ {
nixosConfigurations = {
saarni = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
- specialArgs = {inherit inputs outputs vars nixpkgs-unstable; };
+ specialArgs = {
+ inherit
+ agenix
+ inputs
+ outputs
+ vars
+ pkgs-unstable
+ ;
+ };
modules = [
./hosts/saarni
+ ./roles/shared.nix
+ agenix.nixosModules.default
+ home-manager.nixosModules.home-manager
+ {
+ home-manager = {
+ useGlobalPkgs = true;
+ useUserPackages = true;
+ sharedModules = [ inputs.agenix.homeManagerModules.default ];
+ extraSpecialArgs = {
+ inherit
+ vars
+ inputs
+ outputs
+ pkgs-unstable
+ ;
+ };
+ users = {
+ petri = import ./home;
+ };
+ };
+ }
+ ];
+ };
+ pihlaja = nixpkgs.lib.nixosSystem {
+ system = "x86_64-linux";
+ specialArgs = {
+ inherit
+ inputs
+ outputs
+ vars
+ pkgs-unstable
+ ;
+ };
+ modules = [
+ ./hosts/pihlaja
+ ./roles/shared.nix
+ agenix.nixosModules.default
+ home-manager.nixosModules.home-manager
{
- home-manager.useGlobalPkgs = true;
- home-manager.useUserPackages = true;
+ home-manager = {
+ useGlobalPkgs = true;
+ useUserPackages = true;
+ sharedModules = [ inputs.agenix.homeManagerModules.default ];
+ extraSpecialArgs = {
+ inherit
+ vars
+ inputs
+ outputs
+ pkgs-unstable
+ ;
+ };
+ users = {
+ petri = import ./home;
+ };
+ };
+ }
+ #{
+ #nixpkgs = {
+ # config.cudaSupport = true;
+ #hostPlatform = {
+ # aesSupport = true;
+ # avx2Support = true;
+ # # useLLVM = true;
+ # gcc = {
+ # arch = "x86-64-v3";
+ # };
+ # system = "x86_64-linux";
+ # linker = "mold";
+ # libc = "glibc";
+ #};
+ #};
+ #}
+ ];
+ };
+ kataja = nixpkgs.lib.nixosSystem {
+ system = "x86_64-linux";
+ specialArgs = {
+ inherit
+ inputs
+ outputs
+ vars
+ pkgs-unstable
+ ;
+ };
+ modules = [
+ ./hosts/kataja
+ ./roles/shared.nix
+ agenix.nixosModules.default
+ home-manager.nixosModules.home-manager
+ {
+ home-manager = {
+ useGlobalPkgs = true;
+ useUserPackages = true;
+ sharedModules = [ inputs.agenix.homeManagerModules.default ];
+ extraSpecialArgs = {
+ inherit
+ inputs
+ outputs
+ vars
+ pkgs-unstable
+ ;
+ };
+ users = {
+ petri = import ./home;
+ };
+ };
+ }
+ {
+ nixpkgs = {
+ config.rocmSupport = true;
+ #hostPlatform = {
+ # aesSupport = true;
+ # avx2Support = true;
+ # # useLLVM = true;
+ # gcc = {
+ # arch = "x86-64-v3";
+ # };
+ # system = "x86_64-linux";
+ # linker = "mold";
+ # libc = "glibc";
+ #};
+ };
}
];
};
};
};
}
-
diff --git a/home/alacritty/default.nix b/home/alacritty/default.nix
index 5bed57a..6fc9031 100644
--- a/home/alacritty/default.nix
+++ b/home/alacritty/default.nix
@@ -4,8 +4,75 @@
programs.alacritty = {
enable = true;
settings = {
- selection = {
- save_to_clipboard = true;
+ general = {
+ live_config_reload = true;
+ };
+ font = {
+ normal = {
+ family = "Fira Code";
+ style = "Regular";
+ };
+ size = 11;
+ };
+ scrolling.multiplier = 9;
+ selection.save_to_clipboard = true;
+ window = {
+ opacity = 0.9;
+ };
+ colors = {
+ primary = {
+ background = "#2e3440";
+ foreground = "#d8dee9";
+ dim_foreground = "#a5abb6";
+ };
+ cursor = {
+ text = "#2e3440";
+ cursor = "#d8dee9";
+ };
+ vi_mode_cursor = {
+ text = "#2e3440";
+ cursor = "#d8dee9";
+ };
+ selection = {
+ text = "CellForeground";
+ background = "#4c566a";
+ };
+ search = {
+ matches = {
+ foreground = "CellBackground";
+ background = "#88c0d0";
+ };
+ };
+ normal = {
+ black = "#3b4252";
+ red = "#bf616a";
+ green = "#a3be8c";
+ yellow = "#ebcb8b";
+ blue = "#81a1c1";
+ magenta = "#b48ead";
+ cyan = "#88c0d0";
+ white = "#e5e9f0";
+ };
+ bright = {
+ black = "#4c566a";
+ red = "#bf616a";
+ green = "#a3be8c";
+ yellow = "#ebcb8b";
+ blue = "#81a1c1";
+ magenta = "#b48ead";
+ cyan = "#8fbcbb";
+ white = "#eceff4";
+ };
+ dim = {
+ black = "#373e4d";
+ red = "#94545d";
+ green = "#809575";
+ yellow = "#b29e75";
+ blue = "#68809a";
+ magenta = "#8c738c";
+ cyan = "#6d96a5";
+ white = "#aeb3bb";
+ };
};
};
};
diff --git a/home/cargo/default.nix b/home/cargo/default.nix
new file mode 100644
index 0000000..1af2f7f
--- /dev/null
+++ b/home/cargo/default.nix
@@ -0,0 +1,13 @@
+{ inputs, pkgs, ... }:
+{
+ xdg.configFile."/home/petri/.cargo/config.toml".text = ''
+ [registry]
+ global-credential-providers = ["cargo:token"]
+
+ [registries.relesoft]
+ index = "sparse+https://git.relesoft.io/api/packages/relesoft/cargo/"
+
+ [unstable]
+ gc = true
+ '';
+}
diff --git a/home/chawan/default.nix b/home/chawan/default.nix
new file mode 100644
index 0000000..b343c11
--- /dev/null
+++ b/home/chawan/default.nix
@@ -0,0 +1,22 @@
+{ vars, pkgs, ... }:
+
+{
+ xdg.configFile."/home/petri/.config/chawan/config.toml".text = ''
+ [buffer]
+ autofocus = true
+ images = true
+ styling = true
+ scripting = true
+ history = true
+
+ [input]
+ vi-numeric-prefix = true
+
+ [page]
+ # Here, the arrow function will be called with the vi numbered prefix if
+ # one was input, and with no argument otherwise.
+ # The numeric prefix can never be zero, so it is safe to test for undefined
+ # using the ternary operator.
+ G = 'n => n ? pager.gotoLine(n) : pager.cursorLastLine()'
+ '';
+}
diff --git a/home/conky/default.nix b/home/conky/default.nix
new file mode 100644
index 0000000..1fc628e
--- /dev/null
+++ b/home/conky/default.nix
@@ -0,0 +1,76 @@
+{
+ config,
+ pkgs-unstable,
+ lib,
+ ...
+}:
+let
+ luaMain = builtins.readFile ./main.lua;
+in
+{
+ services.conky = {
+ enable = true;
+ package =
+ (pkgs-unstable.conky.override {
+ curlSupport = true;
+ journalSupport = true;
+ luaCairoSupport = true;
+ luaSupport = true;
+ pulseSupport = true;
+ waylandSupport = true;
+ x11Support = true;
+ }).overrideAttrs
+ (old: {
+ buildInputs = old.buildInputs ++ [
+ pkgs-unstable.cairo
+ pkgs-unstable.wayland
+ pkgs-unstable.libGL
+ pkgs-unstable.expat
+ pkgs-unstable.xorg.libXfixes
+ ];
+ });
+
+ # https://conky.cc/config_settings
+ extraConfig = ''
+ conky.config = {
+ -- wayland
+ out_to_wayland = false,
+ out_to_x = true,
+ own_window_class = 'conky',
+ own_window_type ='override',
+ own_window = true,
+ own_window_transparent = true,
+ own_window_hints = 'undecorated,sticky,below,skip_taskbar,skip_pager',
+ double_buffer = true,
+
+ alignment = "top_right",
+ gap_x = 60,
+ gap_y = 60,
+ minimum_width = 400,
+ minimum_height = 200,
+ maximum_width = 400,
+
+ -- Colors and fonts
+ draw_shades = false,
+ draw_outline = false,
+ draw_borders = false,
+ default_color = "white",
+ default_shade_color = "black",
+ default_outline_color = "black",
+ color1 = "lightblue",
+
+ -- Text
+ use_xft = yes,
+ font = "Liberation Mono:size=10",
+ uppercase = false,
+
+ -- Lua configuration
+ lua_load = '${config.home.homeDirectory}/.config/conky/main.lua',
+ lua_draw_hook_post = "conky_main"
+ };
+
+ conky.text = [[ ]]
+ '';
+ };
+ xdg.configFile."conky/main.lua".text = luaMain;
+}
diff --git a/home/conky/main.lua b/home/conky/main.lua
new file mode 100644
index 0000000..04eac69
--- /dev/null
+++ b/home/conky/main.lua
@@ -0,0 +1,45 @@
+require("cairo")
+require("cairo_xlib")
+
+function conky_main()
+ if conky_window == nil then
+ print("No window")
+ return
+ end
+
+ local cairo_surface = cairo_xlib_surface_create(
+ conky_window.display,
+ conky_window.drawable,
+ conky_window.visual,
+ conky_window.width,
+ conky_window.height
+ )
+ local c = cairo_create(cairo_surface)
+
+ cairo_select_font_face(c, "Liberation Mono", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(c, 12)
+ cairo_set_source_rgba(c, 1, 1, 1, 1)
+ cairo_move_to(c, 100, 100)
+ cairo_show_text(c, "hello world")
+ cairo_stroke(c)
+
+ -- Settings.
+ local line_width = 5
+ local top_left_x = 20
+ local top_left_y = 20
+ local rec_width = 100
+ local rec_height = 50
+ local red = 1
+ local green = 0
+ local blue = 0
+ local alpha = 1
+
+ -- Draw it.
+ cairo_set_line_width(c, line_width)
+ cairo_rectangle(c, top_left_x, top_left_y, rec_width, rec_height)
+ cairo_set_source_rgba(c, red, green, blue, alpha)
+
+ cairo_destroy(c)
+ cairo_surface_destroy(cairo_surface)
+ print("Draw")
+end
diff --git a/home/default.nix b/home/default.nix
index 15054ab..0c845ca 100644
--- a/home/default.nix
+++ b/home/default.nix
@@ -1,71 +1,761 @@
-{ inputs, lib, config, vars, pkgs, ... }:
+{
+ inputs,
+ lib,
+ config,
+ vars,
+ pkgs,
+ pkgs-unstable,
+ ...
+}:
{
- imports = [
- ./hyprland
- ./waybar
+ imports = [
+ # ./alacritty
+ # ./conky
+ # ./helix
+ # ./lf
+ # ./quickshell
+ ./cargo
+ ./chawan
./dunst
- ./lock
- ./git
- ./helix
+ ./firefox
./fish
- ./alacritty
+ ./git
+ ./hyprland
+ ./hyprlock
+ ./mail
+ ./mpv
+ ./newsboat
+ ./nvim
+ ./tenere
+ ./ticker
+ ./vale
+ ./walker
+ ./wallpapers
+ ./waybar
];
+ age = {
+ identityPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
+ secrets = {
+ miniflux_api_key = {
+ path = "${config.home.homeDirectory}/.secrets/miniflux_api_key";
+ file = ../secrets/miniflux_api_key.age;
+ };
+ openai_auth_token.file = ../secrets/openai_auth_token.age;
+ relesoft_cargo_token.file = ../secrets/relesoft_cargo_token.age;
+ shiori_password.file = ../secrets/shiori_password.age;
+ unsplash_access_key.file = ../secrets/unsplash_access_key.age;
+ gmail = {
+ file = ../secrets/gmail.age;
+ path = "${config.home.homeDirectory}/.secrets/gmail";
+ mode = "600";
+ };
+ relesoft = {
+ file = ../secrets/relesoft.age;
+ path = "${config.home.homeDirectory}/.secrets/relesoft";
+ mode = "600";
+ };
+ };
+ };
+
+ gtk = {
+ enable = true;
+ theme = {
+ package = pkgs.whitesur-gtk-theme;
+ name = "WhiteSur-Dark";
+ };
+ iconTheme = {
+ package = pkgs.nordzy-icon-theme;
+ name = "Nordzy";
+ };
+ font = {
+ package = pkgs.roboto-flex;
+ name = "Roboto Flex 11";
+ };
+ };
+
+ qt = {
+ enable = true;
+ style = {
+ package = pkgs.darkly;
+ name = "Darkly";
+ };
+ };
+
+ programs = {
+ element-desktop = {
+ enable = true;
+ settings = {
+ default_theme = "dark";
+ };
+ };
+ zellij = {
+ # modern tmux
+ enable = true;
+ enableFishIntegration = false;
+ };
+ zoxide = {
+ # smarter cd command
+ enable = true;
+ enableFishIntegration = true;
+ enableNushellIntegration = true;
+ };
+ nushell = {
+ enable = true;
+ extraConfig = ''
+ $env.config.hooks.command_not_found = source ${pkgs.nix-index}/etc/profile.d/command-not-found.nu
+ let carapace_completer = {|spans|
+ carapace $spans.0 nushell ...$spans | from json
+ }
+ $env.config = {
+ show_banner: false,
+ completions: {
+ case_sensitive: false # case-sensitive completions
+ quick: true # set to false to prevent auto-selecting completions
+ partial: true # set to false to prevent partial filling of the prompt
+ algorithm: "fuzzy" # prefix or fuzzy
+ external: {
+ enable: true
+ max_results: 100
+ completer: $carapace_completer # check 'carapace_completer'
+ }
+ }
+ }
+ $env.PATH = ($env.PATH |
+ split row (char esep) |
+ prepend /home/petri/.apps |
+ append /usr/bin/env
+ )
+ '';
+ };
+ carapace = {
+ enable = true;
+ enableNushellIntegration = true;
+ enableFishIntegration = true;
+ };
+ gpg = {
+ enable = true;
+ settings = {
+ no-comments = true;
+ };
+ };
+ fzf = {
+ enable = true;
+ enableFishIntegration = true; # use CTRL+t for files, CTRL + r for history
+ defaultOptions = [
+ "--style full"
+ "--preview '${pkgs.pistol}/bin/pistol {}'"
+ ];
+ };
+ yazi = {
+ # file browser
+ enable = true;
+ enableNushellIntegration = true;
+ enableFishIntegration = true;
+ settings = {
+ manager = {
+ show_hidden = true;
+ show_symlink = true;
+ };
+ preview = {
+ image_filter = "lanczos3";
+ image_quality = 90;
+ };
+ };
+ };
+ atuin = {
+ enable = true;
+ enableFishIntegration = true;
+ enableNushellIntegration = true;
+ daemon = {
+ enable = true;
+ };
+ settings = {
+ auto_sync = true;
+ sync_address = "https://atuin.tammi.cc";
+ sync_frequency = "5m";
+ style = "compact";
+ workspaces = true;
+ };
+ };
+ starship = {
+ enable = true;
+ enableFishIntegration = true;
+ enableTransience = true;
+ enableNushellIntegration = true;
+ };
+ eza = {
+ enable = true;
+ enableFishIntegration = true;
+ enableNushellIntegration = true;
+ git = true;
+ icons = "auto";
+ extraOptions = [
+ "--color=always"
+ "--tree"
+ "--level=1"
+ "--group-directories-first"
+ "--dereference"
+ ];
+ };
+ nix-index = {
+ enable = true;
+ enableFishIntegration = true;
+ };
+ pistol.enable = true;
+ ghostty = {
+ enable = true;
+ installVimSyntax = true;
+ enableFishIntegration = true;
+ settings = {
+ font-family = "Iosevka Nerd Font";
+ font-size = 12;
+ mouse-hide-while-typing = true;
+ copy-on-select = true;
+ theme = "nord";
+ };
+ themes = {
+ nord = {
+ background = "#2e3440";
+ foreground = "#d8dee9";
+ selection-background = "#3f4758";
+ selection-foreground = "#d8dee9";
+ cursor-color = "#d8dee9";
+ cursor-text = "#2e3440";
+ palette = [
+ "0=#3b4252"
+ "1=#bf616a"
+ "2=#a3be8c"
+ "3=#ebcb8b"
+ "4=#81a1c1"
+ "5=#b48ead"
+ "6=#88c0d0"
+ "7=#e5e9f0"
+ "8=#4c566a"
+ "9=#bf616a"
+ "10=#a3be8c"
+ "11=#ebcb8b"
+ "12=#81a1c1"
+ "13=#b48ead"
+ "14=#8fbcbb"
+ "15=#eceff4"
+ ];
+ };
+ };
+ };
+ ssh = {
+ enable = true;
+ compression = true;
+ serverAliveInterval = 10;
+ controlMaster = "auto";
+ controlPersist = "10m";
+ };
+
+ chromium = {
+ enable = true;
+ package = pkgs.chromium;
+ commandLineArgs = [
+ "--load-media-router-component-extension=1"
+ "--enable-features=VaapiVideoDecodeLinuxGL,VaapiVideoEncoder,Vulkan,VulkanFromANGLE,DefaultANGLEVulkan,VaapiIgnoreDriverChecks,VaapiVideoDecoder,PlatformHEVCDecoderSupport,UseMultiPlaneFormatForHardwareVideo"
+ "--enable-features=UseOzonePlatform --ozone-platform=wayland"
+ "--enable-media-router"
+ "--enable-smooth-scrolling"
+ "--force-dark-mode"
+ ];
+ dictionaries = [ pkgs.hunspellDictsChromium.en_US ];
+ extensions = [
+ # vimium
+ # https://chromewebstore.google.com/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb
+ "dbepggeogbaibhgnhhndojpepiihcmeb"
+
+ # bitwarden
+ # https://chromewebstore.google.com/detail/bitwarden-password-manage/nngceckbapebfimnlniiiahkandclblb
+ "nngceckbapebfimnlniiiahkandclblb"
+
+ # ublock origin lite
+ # https://chromewebstore.google.com/detail/ublock-origin-lite/ddkjiahejlhfcafbddmgiahcphecmpfh
+ "ddkjiahejlhfcafbddmgiahcphecmpfh"
+
+ # decentraleyes
+ # https://chromewebstore.google.com/detail/decentraleyes/ldpochfccmkkmhdbclfhpagapcfdljkj
+ "ldpochfccmkkmhdbclfhpagapcfdljkj"
+
+ # redeck
+ # https://chromewebstore.google.com/detail/readeck/jnmcpmfimecibicbojhopfkcbmkafhee?pli=1
+ "jnmcpmfimecibicbojhopfkcbmkafhee"
+ ];
+ };
+
+ home-manager.enable = true;
+ };
+
+ services = {
+ mpd-mpris.enable = true;
+ mpd = {
+ enable = true;
+ musicDirectory = "/media/skydrive/Music/";
+ extraArgs = [ "--verbose" ];
+ network = {
+ listenAddress = "any";
+ startWhenNeeded = true;
+ port = 6600;
+ };
+ };
+
+ psd = {
+ enable = true;
+ backupLimit = 5;
+ };
+ gpg-agent = {
+ defaultCacheTtl = 1800;
+ enable = true;
+ enableFishIntegration = true;
+ enableSshSupport = true;
+ pinentry.package = pkgs.pinentry-gnome3;
+ };
+ mpris-proxy.enable = true;
+ clipse = {
+ enable = true;
+ };
+ };
+
+ manual = {
+ html.enable = true;
+ json.enable = true;
+ };
+
home = {
+ enableNixpkgsReleaseCheck = true;
+ stateVersion = "25.05";
+ shell = {
+ enableFishIntegration = true;
+ enableNushellIntegration = true;
+ };
username = "${vars.user}";
sessionVariables = {
- EDITOR = "${pkgs.helix}/bin/hx";
- PAGER = "${pkgs.moar}/bin/moar";
+ CC = "clang";
+ BROWSER = "${pkgs.firefox}/bin/firefox";
+ CARGO_REGISTRIES_RELESOFT_CREDENTIAL_PROVIDER = "cargo:token";
+ CARGO_REGISTRIES_RELESOFT_INDEX = "sparse+https://git.relesoft.io/api/packages/relesoft/cargo/";
+ CARGO_REGISTRIES_RELESOFT_IO_PROTOCOL = "sparse";
+ CARGO_REGISTRIES_RELESOFT_TOKEN = "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.relesoft_cargo_token.path})";
+ CURSOR_SIZE = "16";
DEFAULT_BROWSER = "${pkgs.firefox}/bin/firefox";
- NIXOS_OZONE_WL = "1";
- MOZ_USE_XINPUT2 = "1";
- MOZ_ENABLE_WAYLAND = "1";
+ ELECTRON_OZONE_PLATFORM_HINT = "auto";
GDK_SCALE = "1";
- CURSOR_SIZE = "16";
+ MOZ_USE_XINPUT2 = "1";
+ NIXOS_OZONE_WL = "1";
+ PAGER = "${pkgs.nvimpager}/bin/nvimpager";
+ PISTOL_CHROMA_FORMATTER = "terminal256";
+ PISTOL_CHROMA_STYLE = "monokai";
+ VISUAL = "${pkgs.neovim}/bin/nvim";
+ OPENAI_API_KEY = "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.openai_auth_token.path})";
+ SHIORI_PASSWORD = "$(${pkgs.coreutils}/bin/cat ${config.age.secrets.shiori_password.path})";
};
+
pointerCursor = {
package = pkgs.bibata-cursors;
gtk.enable = true;
name = "Bibata-Modern-Ice";
size = 22;
};
+
homeDirectory = "/home/${vars.user}";
packages = with pkgs; [
- clang
- gnumake
- biome
- go
- mpv
- brightnessctl
- bluez
- element-desktop-wayland
- yamlfmt
- dprint
- nodePackages.jsdoc
- nodePackages.typescript-language-server
- alacritty
- pavucontrol
- moar
- zathura
- grim
- slurp
- fd
- wl-clipboard
+ # tui tools
+ bluetui # bluetooth management
+ chawan # browser, currently crashes
+ clipse # clipboard management
+ impala # iwd terminal ui
+ lux # youtube-downloader
+ pkgs-unstable.nom # rss reader
+ rmpc # tui for mpd
+ rtorrent # torrent
+ starship # shell aid
+ systemctl-tui
+ tenere # llm tui
+ ticker # stock ticker
+ wthrr # weather forecast from openmeteo
+ yazi # file manager
+
+ # gui
+ bitwarden # password maanger
+ ghostty # terminal emulator
+ hypridle
+ hyprlock
hyprpaper
- waybar
- wofi
- dunst
+ libreoffice
+ lxqt.pavucontrol-qt
+ obsidian # note taking application
+ sqlitebrowser # sqlite
+ swayimg # image viewer
+ walker # launcher
+ wireshark
+ zathura # pdf viewer
+
+ # spell checking
+ aspell
+ aspellDicts.en # english aspell dictionary
+ aspellDicts.en-computers
+ aspellDicts.en-science
+ aspellDicts.fi # finnish aspell dictionary
+ enchant # spell cherker interface
+ hunspell
+ hunspellDicts.en-us
+ libvoikko # finnish spelling
+ nuspell # spell checker
+
+ # support
+ ethtool # ethernet driver info
+ ffmpeg
+ libcamera # source camera stack
+ libnotify # send notifications
+ libwebp
+ material-icons
+ material-symbols
+ mermaid-cli # diagrams
+ wl-clipboard
+ wttrbar # weather bar
+
+ # cli-tools
+ appimage-run
+ attic-client # nix cache client
+ brightnessctl
+ cloc # count llines of code
+ dmidecode # memory information
+ espflash # ESP32 flasher
+ espup # ESP32 development
+ eza # modern file list
+ fastfetch # system information
+ fd # find files by filenames
+ file # show the type of file
+ font-awesome # icons
+ gat # cat but with extra features
+ grim # screen capture
+ inxi # system innformation
+ jq # commandline json parser
+ kramdown-asciidoc # command to convert markdown to asciidoc
+ minio-client
+ moar # less replacement
+ ntfy-sh # notifications
+ nvimpager # use neovim as pager
+ openai-whisper-cpp
+ pistol # file preview
+ pkg-config # needed for compilation
+ pkgs-unstable.goose-cli # llm cli
+ pkgs-unstable.vale # spellcheck
+ pkgs-unstable.valeStyles.microsoft # spellcheck linters
+ pkgs-unstable.valeStyles.write-good # spellcheck linters
+ playerctl
+ poppler-utils # pdf utilities
+ rdrview # extract content from web page
+ reader # extract web page and pipe
+ ripgrep # faster grep
+ slurp # screen are capture
+ sqlite # simple database
+ timg # show image in shell
+ wlogout # logout helper
+ xdg-utils
+ xh # curl replacement
+ xxd # view binary files
+ zellij # terminal multiplexer
+
+ # Development
+ antora # documentation tool for asciidoc
+ asciidoctor-with-extensions # asciidoc is a markdown replacement
+ clang # c and rust
+ cmake # c
+ couchbase-shell # couchbase
+ d2 # graphs
+ gnumake # makefile
+ lean4 # theorem prover - broken
+ lld # c and rust linker
+ nodePackages.jsdoc # javascript documentation
+ pkgs-unstable.go
+ pkgs-unstable.golint # linter for go
+ uv # python package installer
];
};
- xdg.mimeApps = {
+ fonts = {
+ fontconfig = {
+ enable = true;
+ defaultFonts = {
+ serif = [ "Roboto Serif" ];
+ sansSerif = [ "Roboto Flex" ];
+ monospace = [ "Iosevka Nerd Font" ];
+ emoji = [ "Twitter Color Emoji" ];
+ };
+ };
+ };
+
+ xdg = {
enable = true;
- defaultApplications = {
- "application/pdf" = "zathura.desktop";
+ autostart.enable = true;
+ portal = {
+ enable = true;
+ xdgOpenUsePortal = true;
+ config.common = {
+ default = [ "*" ];
+ "org.freedesktop.impl.portal.FileChooser" = "termfilechooser";
+ };
+ extraPortals = with pkgs; [
+ xdg-desktop-portal-wlr
+ xdg-desktop-portal-termfilechooser
+ ];
+ };
+ desktopEntries = {
+ nvim = {
+ name = "nvim";
+ genericName = "Editor";
+ exec = ''${pkgs.neovim}/bin/nvim'';
+ terminal = true;
+ categories = [ ];
+ comment = "A text editor";
+ };
+ };
+
+ mimeApps = {
+ enable = true;
+ defaultApplications = {
+ "application/pdf" = [ "org.pwmt.zathura.desktop" ];
+ "inode/directory" = "yazi.desktop";
+ "image/jpeg" = [ "swayimg.desktop" ];
+ "image/png" = [ "swayimg.desktop" ];
+ "image/webp" = [ "swayimg.desktop" ];
+ "text/html" = [ "firefox.desktop" ];
+ "text/json" = [ "nvim.desktop" ];
+ "text/markdown" = "nvim.desktop";
+ "text/plain" = "nvim.desktop";
+ "video/mp4" = "mpv.desktop";
+ "video/webm" = "mpv.desktop";
+ "x-scheme-handler/about" = "firefox.desktop";
+ "x-scheme-handler/http" = "firefox.desktop";
+ "x-scheme-handler/https" = "firefox.desktop";
+ "x-scheme-handler/unknown" = "firefox.desktop";
+ };
+ };
+ };
+ wayland.windowManager.hyprland.systemd.variables = [ "--all" ];
+ systemd.user = {
+ startServices = "sd-switch";
+ };
+
+ xdg.configFile."nom/config.yml".text = ''
+ autoread: true
+ ordering: desc
+ showread: false
+ openers:
+ - regex: "youtube"
+ cmd: "mpv %s"
+ - regex: ".*"
+ cmd: "${pkgs.firefox}/bin/firefox %s"
+ refreshinterval: 5
+ filtering:
+ defaultIncludeFeedName: true
+ backends:
+ miniflux:
+ host: https://flux.tammi.cc
+ api_key: $(${pkgs.coreutils}/bin/cat ${config.age.secrets.miniflux_api_key.path})
+ '';
+
+ systemd.user.services.ntfy-client = {
+ Unit = {
+ Description = "ntfy client subscriber";
+ After = [ "graphical.target" ];
+ };
+
+ Install = {
+ WantedBy = [ "default.target" ];
+ };
+
+ Service = {
+ ExecStart = "${pkgs.ntfy-sh}/bin/ntfy subscribe --config /home/petri/.config/ntfy/client.yml --from-config";
+ Restart = "on-failure";
+ RestartSec = "10s";
+ ProtectSystem = "strict";
+ ProtectHome = "read-only";
+ PrivateTmp = true;
+ NoNewPrivileges = true;
+ RestrictRealtime = true;
+ LockPersonality = true;
+ };
+ };
+
+ xdg.configFile."xdg-desktop-portal-termfilechooser/config".text =
+ let
+ launcherDeps = pkgs.buildEnv {
+ name = "yazi-launcher-dependencies";
+ paths = with pkgs; [
+ coreutils
+ yazi
+ gnused
+ bashInteractive
+ ];
+ };
+ in
+ ''
+ [filechooser]
+ env=PATH='${launcherDeps}/bin'
+ env=TERMCMD='${pkgs.ghostty}/bin/ghostty'
+ cmd='${pkgs.xdg-desktop-portal-termfilechooser}/share/xdg-desktop-portal-termfilechooser/yazi-wrapper.sh'
+ default_dir=$HOME/Downloads
+ '';
+
+ systemd.user.services.bt-notify = {
+ Unit = {
+ Description = "Bluetooth Connection & Battery Notifications";
+ After = [ "graphical-session.target" ];
+ ConditionPathExists = "${config.home.homeDirectory}/.local/bin/bt-notify.sh";
+ };
+ Install = {
+ WantedBy = [ "graphical-session.target" ];
+ };
+ Service = {
+ Type = "simple";
+ ExecStart = "${config.home.homeDirectory}/.local/bin/bt-notify.sh";
+ Restart = "always";
+ RestartSec = 5;
};
};
- programs.home-manager.enable = true;
- systemd.user.startServices = "sd-switch";
- home.stateVersion = "23.11";
+
+ home.file."/.local/bin/bt-notify.sh" = {
+ executable = true;
+ text = ''
+ #!/usr/bin/env bash
+ set -euo pipefail
+
+ # Temporary file to remember which devices were connected last loop
+ STATE_FILE="/tmp/bt-connected-state"
+
+ # Ensure the file exists
+ touch "$STATE_FILE"
+
+ # Listen to BlueZ D-Bus signals AND poll for battery (some devices only report on poll)
+ (
+ # D-Bus listener (connection events)
+ ${pkgs.dbus}/bin/dbus-monitor "interface=org.bluez.Device1,member=PropertiesChanged" --system &
+
+ # Polling loop for battery (runs in background)
+ while true; do
+ ${pkgs.bluez}/bin/bluetoothctl devices | cut -f2 -d' ' | while read -r mac; do
+ info=$(${pkgs.bluez}/bin/bluetoothctl info "$mac")
+ name=$(echo "$info" | grep -i "Alias" | cut -d' ' -f2-)
+ connected=$(echo "$info" | grep -i "Connected" | awk '{print $2}')
+ battery_line=$(echo "$info" | grep -i "Battery Percentage" || true)
+ battery=$(echo "$battery_line" | awk '{print $3}' | tr -d '()')
+
+ # --- Connection change detection ---
+ if [[ "$connected" == "yes" ]] && ! grep -q "^$mac$" "$STATE_FILE"; then
+ ${pkgs.libnotify}/bin/notify-send -a "Bluetooth" "Connected" "$name"
+ echo "$mac" >> "$STATE_FILE"
+ elif [[ "$connected" == "no" ]] && grep -q "^$mac$" "$STATE_FILE"; then
+ ${pkgs.libnotify}/bin/notify-send -a "Bluetooth" "Disconnected" "$name"
+ sed -i "/^$mac$/d" "$STATE_FILE"
+ fi
+
+ # --- Low battery warning ---
+ if [[ -n "$battery" && "$battery" -lt 20 ]]; then
+ ${pkgs.libnotify}/bin/notify-send -u critical -a "Bluetooth" "Low Battery" "$name: ''${battery}%"
+ fi
+ done
+ sleep 10
+ done &
+ ) | while read -r line; do
+ # Parse D-Bus PropertiesChanged for Connected
+ if echo "$line" | grep -q '"Connected"'; then
+ path=$(echo "$line" | grep -oP 'path="\K[^"]+')
+ mac=$(basename "$path")
+ info=$(${pkgs.bluez}/bin/bluetoothctl info "$mac")
+ name=$(echo "$info" | grep -i "Alias" | cut -d' ' -f2-)
+ connected=$(echo "$info" | grep -i "Connected" | awk '{print $2}')
+
+ if [[ "$connected" == "yes" ]] && ! grep -q "^$mac$" "$STATE_FILE"; then
+ ${pkgs.libnotify}/bin/notify-send -a "Bluetooth" "Connected" "$name"
+ echo "$mac" >> "$STATE_FILE"
+ elif [[ "$connected" == "no" ]] && grep -q "^$mac$" "$STATE_FILE"; then
+ ${pkgs.libnotify}/bin/notify-send -a "Bluetooth" "Disconnected" "$name"
+ sed -i "/^$mac$/d" "$STATE_FILE"
+ fi
+ fi
+ done
+ '';
+ };
+
+ xdg.configFile."goose/config.yaml".text = ''
+ OPENAI_BASE_PATH: v1/chat/completions
+ extensions:
+ developer:
+ available_tools: []
+ bundled: true
+ description: null
+ display_name: Developer
+ enabled: true
+ name: developer
+ timeout: 300
+ type: builtin
+ computercontroller:
+ bundled: true
+ display_name: Computer Controller
+ enabled: true
+ name: computercontroller
+ timeout: 300
+ type: builtin
+ memory:
+ available_tools: []
+ bundled: true
+ description: Tools to save and retrieve durable memories
+ display_name: Memory
+ enabled: true
+ name: memory
+ timeout: 80
+ type: builtin
+ todo:
+ available_tools: []
+ bundled: true
+ description: Enable a todo list for Goose so it can keep track of what it is doing
+ enabled: true
+ name: todo
+ type: platform
+ chatrecall:
+ available_tools: []
+ bundled: true
+ description: Search past conversations and load session summaries for contextual memory
+ enabled: true
+ name: chatrecall
+ type: platform
+ extensionmanager:
+ available_tools: []
+ bundled: true
+ description: Enable extension management tools for discovering, enabling, and disabling extensions
+ enabled: true
+ name: Extension Manager
+ type: platform
+ autovisualiser:
+ available_tools: []
+ bundled: true
+ description: Data visualisation and UI generation tools
+ display_name: Auto Visualiser
+ enabled: true
+ name: autovisualiser
+ timeout: 300
+ type: builtin
+ GOOSE_MODE: auto
+ GOOSE_PROVIDER: openai
+ GOOSE_MODEL: gpt-4.1-mini
+ OPENAI_HOST: https://api.openai.com
+ '';
+
+ xdg.configFile."ntfy/client.yml".text = ''
+ default-host: https://ntfy.tammi.cc
+
+ subscribe:
+ - topic: 77WxlkfsTzGrbYFw
+ host: https://ntfy.tammi.cc
+ since: 1h # (optional) only get messages from the last hour
+ persist: true
+ keepalive: 45s # (optional) keepalive interval
+ format: markdown # (optional) output formatting: text, json, markdown, etc.
+ '';
}
diff --git a/home/dunst/default.nix b/home/dunst/default.nix
index 8a5f4ad..4843a90 100644
--- a/home/dunst/default.nix
+++ b/home/dunst/default.nix
@@ -1,74 +1,121 @@
-{ config, pkgs, ... }: {
+{ config, pkgs, ... }:
+{
services.dunst = {
enable = true;
+ iconTheme = {
+ name = "Twitter Color Emoji";
+ package = pkgs.twitter-color-emoji;
+ };
+
settings = {
global = {
- alignment = "center";
- allow_markup = true;
- browser =
- "${config.programs.firefox.package}/bin/firefox -new-tab";
- bounce_freq = 0;
- corner_radius = 5;
- dmenu = "${pkgs.rofi}/bin/rofi -dmenu";
- follow = "mouse";
- font = "Fira Code 10";
- format = ''
- <b>%s</b>
- %b
- %p'';
- frame_color = "#555555";
- frame_width = 2;
- geometry = "400-30+30";
- horizontal_padding = 15;
- icon_position = "off";
- idle_threshold = 120;
- ignore_newline = false;
- indicate_hidden = true;
- line_height = 0;
- markup = "full";
- max_icon_size = 130;
- padding = 15;
- separator_color = "frame";
- separator_height = 2;
- show_age_threshold = 60;
- shrink = true;
- sort = true;
- startup_notification = false;
- sticky_history = true;
+ ### Display ###
+ monitor = 0;
+ follow = "none";
+ width = "(100, 500)";
+ height = "(0, 300)";
+ origin = "top-right";
+ offset = "(5, 15)";
+ scale = 0;
+ notification_limit = 10;
+
+ ### Progress bar ###
+ progress_bar = true;
+ progress_bar_height = 14;
+ progress_bar_frame_width = 0;
+ progress_bar_min_width = 100;
+ progress_bar_max_width = 500;
+ progress_bar_corner_radius = 50;
+ progress_bar_corners = "bottom-left,top-right";
+ icon_corner_radius = 0;
+ icon_corners = "all";
+
+ indicate_hidden = "yes";
transparency = 0;
- word_wrap = true;
- };
+ separator_height = 6;
+ padding = 10;
+ horizontal_padding = 8;
+ text_icon_padding = 12;
+ frame_width = 1;
+ frame_color = "#a0a0a0";
+ gap_size = 6;
+ separator_color = "frame";
+ corner_radius = 10;
+ corners = "bottom,top-left";
- frame = {
- width = 2;
- color = "#83a598";
+ ### Text ###
+ font = "Iosevka Nerd Font";
+ markup = "full";
+ format = "<b>%s</b>\n%b";
+ alignment = "left";
+ vertical_alignment = "center";
+ word_wrap = "yes";
+ ellipsize = "middle";
+ ignore_newline = "no";
+ line_height = 0;
+ show_age_threshold = -1;
+ stack_duplicates = true;
+ hide_duplicate_count = false;
+ show_indicators = "yes";
+
+ ### Icons ###
+ icon_position = "right";
+ min_icon_size = 32;
+ max_icon_size = 128;
+ enable_recursive_icon_lookup = true;
+ icon_theme = "Twitter Color Emoji";
+
+ ### History ###
+ sticky_history = "yes";
+ history_length = 30;
+
+ ### Behavior ###
+ sort = "yes";
+ idle_threshold = 120;
+ always_run_script = true;
+
+ ### Browser & Menu ###
+ browser = "${pkgs.firefox}/bin/firefox -new-tab";
+ dmenu = "${pkgs.walker}/bin/walker --modules applications,translation,webesearch,clipboard,finder";
+
+ ### Misc ###
+ title = "Dunst";
+ class = "Dunst";
+ startup_notification = true;
+ ignore_dbusclose = false;
+ force_xwayland = false;
+ force_xinerama = false;
+ per_monitor_dpi = false;
+
+ ### Mouse ###
+ mouse_left_click = "close_current";
+ mouse_middle_click = "do_action,close_current";
+ mouse_right_click = "close_all";
};
urgency_low = {
- frame_color = "#fabd2f";
- background = "#282828";
- foreground = "#ebdbb2";
- timeout = 5;
+ background = "#222222";
+ foreground = "#ffffff";
+ highlight = "#722ae6,#e4b5cb";
+ timeout = 20;
};
urgency_normal = {
- background = "#282828";
- foreground = "#ebdbb2";
- timeout = 15;
+ background = "#222222";
+ foreground = "#ffffff";
+ frame_color = "#5e5086";
+ highlight = "#722ae6,#e4b5cb";
+ timeout = 20;
+ override_pause_level = 30;
};
urgency_critical = {
- background = "#282828";
- foreground = "#ebdbb2";
- frame_color = "#CC241D";
+ background = "#222222";
+ foreground = "#ffffff";
+ frame_color = "#d54e53";
+ highlight = "#d54e53,#f0f0f0";
timeout = 0;
- };
-
- shortcuts = {
- close = "ctrl+space";
- close_all = "ctrl+shift+space";
- history = "ctrl+grave";
- context = "ctrl+shift+period";
+ override_pause_level = 60;
};
};
};
diff --git a/home/eww/default.nix b/home/eww/default.nix
new file mode 100644
index 0000000..b6a745c
--- /dev/null
+++ b/home/eww/default.nix
@@ -0,0 +1,197 @@
+{
+ pkgs,
+ lib,
+ config,
+ ...
+}:
+
+let
+ timerScript = pkgs.writeShellScriptBin "timer.sh" ''
+ #!${pkgs.bash}/bin/bash
+ CUR_TIME=$(${pkgs.coreutils}/bin/date +%s)
+ STATUS=$(${pkgs.coreutils}/bin/cat /var/tmp/timer_status || echo "READY")
+
+ if [ "$STATUS" = "READY" ]; then
+ echo ""
+ elif [ "$STATUS" = "FINISHED" ]; then
+ ${pkgs.pulseaudio}/bin/paplay "${config.home.homeDirectory}/.config/eww/timer.wav" &
+ echo "READY" > /var/tmp/timer_status
+ echo ""
+ elif [ "$STATUS" -gt "$CUR_TIME" ]; then
+ DIFF=$(( STATUS - CUR_TIME ))
+ echo $(${pkgs.coreutils}/bin/date -d "@$DIFF" +%M:%S)
+ else
+ echo "FINISHED" > /var/tmp/timer_status
+ echo ""
+ fi
+ '';
+
+ calendarScript = pkgs.writeShellScriptBin "calendar.sh" ''
+ #!${pkgs.bash}/bin/bash
+ ${pkgs.coreutils}/bin/date +'<big>%Y %B</big>\n<tt><small>%d %A</small></tt>'
+ '';
+
+in
+
+{
+ programs.eww = {
+ enable = true;
+ package = pkgs.eww;
+ };
+
+ # Dependencies
+ home.packages = with pkgs; [
+ hyprland
+ brightnessctl
+ lxqt.pavucontrol-qt
+ wttrbar
+ bluetui
+ wlogout
+ walker
+ lm_sensors
+ pulseaudio
+ socat
+ jq
+ procps
+ gawk
+ gnugrep
+ gnused
+ systemd
+ iproute2
+ dbus
+ ];
+
+ home.file.".config/eww/eww.yuck".text = ''
+ ;; Variables and Polls
+ (defpoll time :interval "1s" "${pkgs.coreutils}/bin/date +'%A, %Y-%m-%d %H:%M:%S'")
+ (defpoll weather :interval "3600s" "${pkgs.wttrbar}/bin/wttrbar --main-indicator=FeelsLikeC --date-format=%d.%m.%Y --location=Helsinki | ${pkgs.jq}/bin/jq .FeelsLikeC")
+ (defpoll cpu-usage :interval "2s" "${pkgs.procps}/bin/top -bn1 | ${pkgs.gnugrep}/bin/grep '^%Cpu' | ${pkgs.gawk}/bin/awk '{print 100 - \$8}'")
+ (defpoll memory :interval "2s" "${pkgs.procps}/bin/free -h | ${pkgs.gnugrep}/bin/grep '^Mem:' | ${pkgs.gawk}/bin/awk '{print \$3 \"/\" \$2}'")
+ (defpoll load :interval "2s" "${pkgs.coreutils}/bin/cat /proc/loadavg | ${pkgs.gawk}/bin/awk '{print \$1}'")
+ (defpoll temperature :interval "5s" "${pkgs.lm_sensors}/bin/sensors | ${pkgs.gnugrep}/bin/grep 'Tctl:' | ${pkgs.gawk}/bin/awk '{print \$2}' | ${pkgs.gnused}/bin/sed 's/+//'")
+ (defpoll volume :interval "2s" "${pkgs.pulseaudio}/bin/pactl get-sink-volume @DEFAULT_SINK@ | ${pkgs.gawk}/bin/awk '{print \$5}' | ${pkgs.gnused}/bin/sed 's/%//'")
+ (defpoll mute :interval "2s" "${pkgs.pulseaudio}/bin/pactl get-sink-mute @DEFAULT_SINK@ | ${pkgs.gnugrep}/bin/grep -q 'Mute: yes' && echo 'true' || echo 'false'")
+ (defpoll backlight :interval "2s" "${pkgs.brightnessctl}/bin/brightnessctl g")
+ (defpoll battery :interval "10s" "${pkgs.coreutils}/bin/cat /sys/class/power_supply/BAT0/capacity 2>/dev/null || echo '0'")
+ (defpoll battery-status :interval "10s" "${pkgs.coreutils}/bin/cat /sys/class/power_supply/BAT0/status 2>/dev/null || echo 'Unknown'")
+ (defpoll failed-units :interval "30s" "${pkgs.systemd}/bin/systemctl --failed --no-legend | ${pkgs.coreutils}/bin/wc -l")
+ (defpoll processes :interval "10s" "${pkgs.procps}/bin/ps -e --no-headers | ${pkgs.coreutils}/bin/wc -l")
+ (defpoll sockets :interval "10s" "${pkgs.iproute2}/bin/ss -tuln | ${pkgs.coreutils}/bin/tail -n +2 | ${pkgs.coreutils}/bin/wc -l")
+ (defpoll keyboard-layout :interval "5s" "${pkgs.hyprland}/bin/hyprctl getoption general:kb_layout -j | ${pkgs.jq}/bin/jq -r .str")
+
+ ;; Hyprland workspace tracking
+ (defpoll workspaces :interval "1s" "${pkgs.hyprland}/bin/hyprctl workspaces -j")
+ (defpoll active-workspace :interval "1s" "${pkgs.hyprland}/bin/hyprctl activeworkspace -j | ${pkgs.jq}/bin/jq -r .id")
+ (defpoll current-window :interval "1s" "${pkgs.hyprland}/bin/hyprctl activewindow -j | ${pkgs.jq}/bin/jq -r .title")
+
+ ;; Widgets
+ (defwidget bar []
+ (box :orientation "h" :class "bar" :space-evenly false
+ (box :class "left" :halign "start" :spacing 8
+ ;; Start menu with eventbox for hover
+ (eventbox :onhover "${pkgs.pulseaudio}/bin/paplay ${config.home.homeDirectory}/.config/eww/beep.ogg &"
+ (button :class "startmenu" :onclick "${pkgs.walker}/bin/walker" "⚡"))
+ (box :class "workspaces" :spacing 4
+ (for ws in workspaces
+ (button :onclick "${pkgs.hyprland}/bin/hyprctl dispatch workspace ''${ws.id}"
+ :class { ws.id == active-workspace ? "active" : "" }
+ "''${ws.name}")))
+ (label :class "clock" :text time)
+ (label :class "timer" :text (timer-widget)))
+
+ (box :class "center" :halign "center"
+ (label :text {current-window ? current-window : "No Windows"}))
+
+ (box :class "right" :halign "end" :spacing 8
+ (label :class "cpu" :text " {cpu-usage}%")
+ (label :class "memory" :text " {memory}")
+ (label :class "temperature" :text " {temperature}")
+ ;; Pulseaudio with eventbox for hover
+ (eventbox :onhover "${pkgs.pulseaudio}/bin/paplay ${config.home.homeDirectory}/.config/eww/beep.ogg &" :onclick "${pkgs.lxqt.pavucontrol-qt}/bin/pavucontrol-qt"
+ (label :class "pulseaudio" :text {mute ? "" : ""} " {volume}%"))
+ (label :class "battery" :text {battery-status == "Charging" ? "󰂄" : "󰁹"} " {battery}%")
+ ;; Bluetooth with eventbox for hover
+ (eventbox :onhover "${pkgs.pulseaudio}/bin/paplay ${config.home.homeDirectory}/.config/eww/beep.ogg &" :onclick "${pkgs.bluetui}/bin/bluetui"
+ (button :class "bluetooth" ""))
+ ;; Exit with eventbox for hover
+ (eventbox :onhover "${pkgs.pulseaudio}/bin/paplay ${config.home.homeDirectory}/.config/eww/beep.ogg &" :onclick "${pkgs.wlogout}/bin/wlogout"
+ (button :class "exit" ""))))
+
+ ;; Timer logic
+ (defpoll timer-display :interval "1s" "${timerScript}/bin/timer.sh")
+
+ (defwidget timer-widget []
+ (label :text timer-display))
+
+ ;; Window
+ (defwindow bar
+ :monitor 0
+ :geometry (geometry :x "0%" :y "0%" :width "100%" :height "40px")
+ :stacking "fg"
+ :windowtype "dock"
+ :reserve (struts :distance "40px" :side "top")
+ (bar))
+ '';
+
+ home.file.".config/eww/eww.css".text = ''
+ * {
+ font-family: "JetBrainsMono Nerd Font", monospace;
+ font-size: 14px;
+ color: #ff9999;
+ background-color: transparent;
+ }
+
+ .bar {
+ background-color: #1a0000;
+ border-bottom: 1px solid #ff4d4d;
+ padding: 0 16px;
+ min-height: 40px;
+ }
+
+ .left, .center, .right {
+ padding: 8px 0;
+ }
+
+ button, label {
+ padding: 4px 12px;
+ border-radius: 4px;
+ background-color: transparent;
+ }
+
+ button:hover, eventbox:hover > * {
+ background-color: rgba(255, 77, 77, 0.2);
+ }
+
+ .active {
+ background-color: rgba(255, 77, 77, 0.3);
+ box-shadow: 0 0 8px rgba(255, 77, 77, 0.5);
+ }
+
+ .cpu, .memory, .temperature, .pulseaudio, .battery {
+ padding: 4px 8px;
+ }
+
+ .exit {
+ background-color: rgba(255, 77, 77, 0.3);
+ margin-left: 8px;
+ }
+
+ .exit:hover {
+ background-color: rgba(255, 77, 77, 0.5);
+ }
+
+ .bluetooth {
+ background-color: rgba(77, 77, 255, 0.2);
+ }
+
+ .bluetooth:hover {
+ background-color: rgba(77, 77, 255, 0.4);
+ }
+ '';
+
+ # Ensure EWW starts with Hyprland
+ wayland.windowManager.hyprland.settings.exec-once = [
+ "${pkgs.eww}/bin/eww daemon"
+ "sleep 1 && ${pkgs.eww}/bin/eww open bar"
+ ];
+}
diff --git a/home/firefox/default.nix b/home/firefox/default.nix
new file mode 100644
index 0000000..228843c
--- /dev/null
+++ b/home/firefox/default.nix
@@ -0,0 +1,164 @@
+{ inputs, pkgs, ... }:
+
+{
+ programs.firefox = {
+ enable = true;
+ languagePacks = [
+ "en-GB"
+ "fi"
+ ];
+ policies = {
+ Cookies.Behavior = "reject-tracker-and-partition-foreign";
+ DisableBuiltinPDFViewer = true;
+ DisableFirefoxStudies = true;
+ DisablePocket = true;
+ DisableTelemetry = true;
+ DontCheckDefaultBrowser = true;
+ EnableTrackingProtection = {
+ Cryptomining = true;
+ Fingerprinting = true;
+ Value = true;
+ };
+ HardwareAcceleration = true;
+ Homepage.StartPage = "previous-session";
+ SearchBar = "unified";
+ TranslateEnabled = false;
+ Preferences = {
+ "browser.in-content.dark-mode" = true;
+ "extensions.autoDisableScopes" = 0;
+ "extensions.update.enabled" = false;
+ "ui.systemUsesDarkTheme" = true;
+ "widget.use-xdg-desktop-portal.file-picker" = 1;
+ };
+ };
+ profiles = {
+ default = {
+ id = 0;
+ name = "default";
+ isDefault = true;
+ extensions.packages = with inputs.firefox-addons.packages."x86_64-linux"; [
+ bitwarden
+ clearurls
+ decentraleyes
+ istilldontcareaboutcookies
+ new-tab-override
+ qwant-search
+ readeck
+ shiori
+ theme-nord-polar-night
+ ublock-origin
+ vimium
+ youtube-high-definition
+ youtube-nonstop
+ youtube-shorts-block
+ ];
+ settings = {
+ "extensions.activeThemeID" = "{758478b6-29f3-4d69-ab17-c49fe568ed80}";
+ "browser.startup.page" = 3;
+ "browser.startup.homepage" = "https://start.tammi.cc";
+ "browser.newtabpage.pinned" = [
+ {
+ title = "Startpage";
+ url = "https://start.tammi.cc";
+ }
+ ];
+ "browser.download.useDownloadDir" = false;
+ "browser.eme.ui.enabled" = false;
+ "browser.search.defaultenginename" = "Searx";
+ "browser.search.order.1" = "Searx";
+ "browser.send_pings" = false;
+ "browser.tabs.crashReporting.sendReport" = false;
+ "browser.translations.neverTranslateLanguages" = "fi";
+ "dom.webgpu.enabled" = true;
+ "experiments.activeExperiment" = false;
+ "experiments.enabled" = false;
+ "experiments.supported" = false;
+ "gfx.webrender.all" = true;
+ "media.eme.enabled" = false;
+ "media.ffmpeg.vaapi.enabled" = true;
+ "media.hardwaremediakeys.enabled" = true;
+ "mousewheel.default.delta_multiplier_y" = 600;
+ "network.allow-experiments" = false;
+ "network.dns.disablePrefetch" = false;
+ "network.trr.bootstrapAddr" = "87.92.94.136";
+ "network.trr.mode" = 0;
+ "privacy.donottrackheader.enabled" = true;
+ "widget.wayland-dmabuf-vaapi.enabled" = true;
+ };
+ search = {
+ force = true;
+ default = "Searx";
+ order = [
+ "Searx"
+ "Nix Packages"
+ "NixOS Options"
+ "Home Manager"
+ "ddg"
+ ];
+ engines = {
+ "Searx" = {
+ urls = [ { template = "https://haku.tammi.cc/?q={searchTerms}"; } ];
+ icon = "https://nixos.wiki/favicon.png";
+ definedAliases = [ "@searx" ];
+ };
+ "Nix Packages" = {
+ icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
+ definedAliases = [ "@np" ];
+ urls = [
+ {
+ template = "https://search.nixos.org/packages";
+ params = [
+ {
+ name = "type";
+ value = "packages";
+ }
+ {
+ name = "query";
+ value = "{searchTerms}";
+ }
+ ];
+ }
+ ];
+ };
+
+ "NixOS Options" = {
+ icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
+ definedAliases = [ "@no" ];
+ urls = [
+ {
+ template = "https://search.nixos.org/options";
+ params = [
+ {
+ name = "channel";
+ value = "unstable";
+ }
+ {
+ name = "query";
+ value = "{searchTerms}";
+ }
+ ];
+ }
+ ];
+ };
+
+ "Home Manager" = {
+ icon = "${pkgs.nixos-icons}/share/icons/hicolor/scalable/apps/nix-snowflake.svg";
+ definedAliases = [ "@hm" ];
+ url = [
+ {
+ template = "https://mipmip.github.io/home-manager-option-search/";
+ params = [
+ {
+ name = "query";
+ value = "{searchTerms}";
+ }
+ ];
+ }
+ ];
+ };
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/home/fish/default.nix b/home/fish/default.nix
index af2a377..08bb295 100644
--- a/home/fish/default.nix
+++ b/home/fish/default.nix
@@ -1,14 +1,112 @@
-{ vars, pkgs, ... }:
+{ config, pkgs, ... }:
{
programs.fish = {
enable = true;
+ shellAliases = {
+ browser = "${pkgs.chawan}/bin/cha";
+ cat = "${pkgs.gat}/bin/gat";
+ gmail = "${pkgs.himalaya}/himalaya envelope list --account gmail --folder gmail/Inbox 'not flag Seen'";
+ grep = "${pkgs.ripgrep}/bin/rg";
+ http = "${pkgs.xh}/bin/xh";
+ journalctl = "${pkgs.systemd}/bin/journalctl --output=short-iso";
+ l = "${pkgs.eza}/bin/eza -l --icons --git -a";
+ llm = "${pkgs.tenere}/bin/tenere";
+ ls = "${pkgs.eza}/bin/eza -a --group-directories-first";
+ lt = "${pkgs.eza}/bin/eza --tree --level=2 --long --icons --git";
+ relesoft = "${pkgs.himalaya}/himalaya envelope list --account relesoft --folder relesoft/Inbox 'not flag Seen'";
+ ssh = "TERM=xterm-256color ${pkgs.openssh}/bin/ssh";
+ stocks = "${pkgs.ticker}/bin/ticker --config /home/petri/.config/ticker/config.yaml";
+ weather = "${pkgs.wthrr}/bin/wthrr --forecast d";
+ wifi = "${pkgs.impala}/bin/impala";
+ youtube = "${pkgs.pipe-viewer}/bin/pipe-viewer";
+ };
+ functions = {
+ doc = ''
+ ${pkgs.asciidoctor}/bin/asciidoctor --backend xhtml5 --out-file - "$argv[1]"|${pkgs.chawan}/bin/cha -T application/xhtml+xml
+ '';
+ bookmark = ''
+ set -l Shiori_URL "https://shiori.tammi.cc"
+ set -l Username "petri"
+ set -l token (${pkgs.curl}/bin/curl -s -X POST -H "Content-Type: application/json" -d '{"username": "'$Username'", "password": "'$SHIORI_PASSWORD'", "remember": true}' $Shiori_URL/api/v1/auth/login | string match -r '(?<="token":")[^"]*')
+ ${pkgs.curl}/bin/curl -s -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d '{ "url": "'$argv[1]'", "createArchive": true, "public": 1 }' $Shiori_URL/api/bookmarks
+ '';
+ share = ''
+ ${pkgs.curl}/bin/curl -X PUT --data-binary @$argv[1] https://bin.tammi.cc >&2
+ '';
+ import = ''
+ ${pkgs.grim}/bin/grim -g "$(${pkgs.slurp}/bin/slurp)" $argv
+ '';
+ readdoc = "${pkgs.reader}/bin/reader $argv --markdown-output | ${pkgs.moar}/bin/moar --wrap --lang markdown";
+ extract = ''
+ function extract
+ switch $argv[1]
+ case "*.tar.bz2"
+ tar xjf $argv[1]
+
+ case "*.tar.gz"
+ tar xzf $argv[1]
+
+ case "*.tar.xz"
+ tar xJf $argv[1]
+
+ case "*.bz2"
+ bunzip2 $argv[1]
+
+ case "*.rar"
+ unrar e $argv[1]
+
+ case "*.gz"
+ gunzip $argv[1]
+
+ case "*.tar"
+ tar xf $argv[1]
+
+ case "*.tbz2"
+ tar xjf $argv[1]
+
+ case "*.tgz"
+ tar xzf $argv[1]
+
+ case "*.zip"
+ unzip $argv[1]
+
+ case "*.Z"
+ uncompress $argv[1]
+
+ case "*.7z"
+ 7z x $argv[1]
+
+ case "*"
+ echo "unknown extension: $argv[1]"
+ end
+ end
+ '';
+ };
interactiveShellInit = ''
- function fish_greeting
- if status is-interactive
- fastfetch
- end
- end
+ set -g fish_key_bindings fish_vi_key_bindings
+
+ set -g __fish_git_prompt_show_informative_status 1
+
+ set -g __fish_git_prompt_color_branch magenta --bold
+ set -g __fish_git_prompt_showupstream "informative"
+ set -g __fish_git_prompt_char_upstream_ahead "↑ "
+ set -g __fish_git_prompt_char_upstream_behind "↓ "
+ set -g __fish_git_prompt_char_upstream_prefix ""
+
+ set -g __fish_git_prompt_char_stagedstate "● "
+ set -g __fish_git_prompt_char_dirtystate "✚"
+ set -g __fish_git_prompt_char_untrackedfiles "…"
+ set -g __fish_git_prompt_char_conflictedstate "✖"
+ set -g __fish_git_prompt_char_cleanstate "✔"
+
+ set -g __fish_git_prompt_color_dirtystate blue
+ set -g __fish_git_prompt_color_stagedstate yellow
+ set -g __fish_git_prompt_color_invalidstate red
+ set -g __fish_git_prompt_color_untrackedfiles $fish_color_normal
+ set -g __fish_git_prompt_color_cleanstate green --bold
+
+ ${pkgs.fastfetch}/bin/fastfetch
'';
};
}
diff --git a/home/git/default.nix b/home/git/default.nix
index d3c605e..6d975e3 100644
--- a/home/git/default.nix
+++ b/home/git/default.nix
@@ -1,11 +1,27 @@
-{pkgs, ...}:{
+{ pkgs, ... }:
+{
programs.git = {
enable = true;
- userName = "Petri Hienonen";
+ aliases = {
+ lg = "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit";
+ };
+ diff-so-fancy.enable = true;
+ maintenance.enable = true;
userEmail = "petri.hienonen@gmail.com";
+ userName = "Petri Hienonen";
extraConfig = {
- init.defaultBranch = "master";
+ core = {
+ editor = "${pkgs.neovim}/bin/nvim";
+ hooksPath = ".githooks/";
+ quotepath = false;
+ };
+ commit.gpgSign = true;
credential.helper = "store";
+ init.defaultBranch = "master";
+ merge.conflictStyle = "diff3";
+ pull.rebase = true;
+ tag.gpgSign = true;
+ user.signingkey = "76028181DD730D0C";
};
};
}
diff --git a/home/helix/default.nix b/home/helix/default.nix
index 2438703..37c8529 100644
--- a/home/helix/default.nix
+++ b/home/helix/default.nix
@@ -1,35 +1,156 @@
+{ inputs, pkgs, ... }:
{
- inputs,
- pkgs,
- ...
-}: {
programs.helix = {
+ extraPackages = [ ];
enable = true;
+ defaultEditor = false;
settings = {
theme = "catppuccin_mocha";
editor = {
+ auto-format = true;
+ auto-pairs = false;
+ bufferline = "multiple";
color-modes = true;
+ completion-replace = true;
+ completion-trigger-len = 1;
cursorline = true;
- cursor-shape = {
- insert = "bar";
- normal = "block";
- select = "underline";
- };
indent-guides.render = true;
- lsp.display-inlay-hints = true;
- statusline.center = ["position-percentage"];
+ soft-wrap.enable = true;
+ statusline.center = [ "position-percentage" ];
true-color = true;
- whitespace.characters = {
- newline = "↴";
- tab = "⇥";
+ whitespace = {
+ render = "all";
+ characters = {
+ newline = "↴";
+ tab = "⇥";
+ };
};
};
-
keys.normal.space.u = {
f = ":format"; # format using LSP formatter
w = ":set whitespace.render all";
W = ":set whitespace.render none";
};
+ editor.cursor-shape = {
+ insert = "bar";
+ normal = "block";
+ select = "underline";
+ };
+ editor.lsp = {
+ display-messages = true;
+ display-inlay-hints = true;
+ display-signature-help-docs = true;
+ };
+ };
+ languages = {
+ language-server = {
+ vale-ls = {
+ command = "${pkgs.vale-ls}/bin/vale-ls";
+ };
+ rust-analyzer = {
+ config = {
+ checkOnSave = {
+ command = "${pkgs.clippy}/bin/clippy";
+ };
+ cargo = {
+ allFeatures = true;
+ };
+ };
+ };
+ biome = {
+ command = "${pkgs.biome}/bin/biome";
+ args = [ "lsp-proxy" ];
+ };
+ ruff = {
+ command = "${pkgs.ruff}/bin/ruff";
+ args = [
+ "server"
+ "--preview"
+ "--isolated"
+ ];
+ };
+ pyright = {
+ command = "${pkgs.pyright}/bin/pyright-langserver";
+ args = [ "--stdio" ];
+ };
+ };
+ language = [
+ {
+ name = "asciidoc";
+ language-servers = [ "vale-ls" ];
+ scope = "source.adoc";
+ file-types = [ "adoc" ];
+ grammar = "asciidoc";
+ }
+ {
+ name = "rust";
+ auto-format = true;
+ }
+ {
+ name = "python";
+ language-servers = [
+ "ruff"
+ "pyright"
+ ];
+ auto-format = true;
+ }
+ {
+ name = "latex";
+ language-servers = [
+ "texlab"
+ "vale-ls"
+ ];
+ auto-format = true;
+ formatter = {
+ command = "${pkgs.tex-fmt}/bin/tex-fmt";
+ args = [
+ "--stdin"
+ "--keep"
+ ];
+ };
+ }
+ {
+ name = "nix";
+ auto-format = true;
+ formatter.command = "${pkgs.nixfmt-rfc-style}/bin/nixfmt";
+ }
+ {
+ name = "markdown";
+ auto-format = true;
+ formatter = {
+ command = "${pkgs.dprint}/bin/dprint";
+ args = [
+ "fmt"
+ "--stdin"
+ "md"
+ ];
+ };
+ language-servers = [
+ "vale-ls"
+ "marksman"
+ ];
+ }
+ {
+ name = "toml";
+ auto-format = true;
+ }
+ {
+ name = "javascript";
+ language-servers = [
+ {
+ name = "typescript-language-server";
+ except-features = [ "format" ];
+ }
+ "biome"
+ ];
+ auto-format = true;
+ }
+ {
+ name = "json";
+ language-servers = [ "biome" ];
+ auto-format = true;
+ }
+ ];
};
};
}
diff --git a/home/hyprland/default.nix b/home/hyprland/default.nix
index e00e794..992fa09 100644
--- a/home/hyprland/default.nix
+++ b/home/hyprland/default.nix
@@ -1,24 +1,110 @@
+{ pkgs, ... }:
{
+ services.hyprpaper = {
+ enable = true;
+ settings = {
+ ipc = "on";
+ preload = [
+ "/etc/nixos/home/wallpapers/owl1.webp"
+ "/etc/nixos/home/wallpapers/owl2.webp"
+ "/etc/nixos/home/wallpapers/owl3.webp"
+ ];
+ wallpaper = [ ", /etc/nixos/home/wallpapers/owl1.webp" ];
+ };
+ };
+
+ services.hyprpolkitagent.enable = true;
+
+ services.hypridle = {
+ enable = true;
+ settings = {
+ general = {
+ after_sleep_cmd = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on";
+ ignore_dbus_inhibit = false;
+ lock_cmd = "${pkgs.procps}/bin/pidof hyprlock || ${pkgs.hyprlock}/bin/hyprlock";
+ };
+
+ listener = [
+ {
+ timeout = 300;
+ on-timeout = "${pkgs.brightnessctl}/bin/brightnessctl -s set 10";
+ on-resume = "${pkgs.brightnessctl}/bin/brightnessctl -r";
+ }
+ {
+ timeout = 600;
+ on-timeout = "${pkgs.systemd}/bin/loginctl lock-session";
+ }
+ {
+ timeout = 900; # 15 minutes
+ on-timeout = "${pkgs.hyprland}/bin/hyprctl dispatch dpms off"; # screen off when timeout has passed
+ on-resume = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on"; # screen on when activity is detected after timeout has fired.
+ }
+ {
+ timeout = 1800;
+ on-timeout = "${pkgs.systemd}/bin/systemctl suspend";
+ }
+ ];
+ };
+ };
wayland.windowManager.hyprland = {
enable = true;
- systemd.enable = true;
+ package = pkgs.hyprland;
+ systemd = {
+ variables = [ "--all" ];
+ enable = true;
+ enableXdgAutostart = true;
+ };
+ extraConfig = ''
+ exec-once = ${pkgs.hyprlock}/bin/hyprlock --immediate;
+ '';
+ xwayland.enable = true;
settings = {
- "exec-once" = "waybar & hyprpaper";
- "monitor" = ",preferred,auto,1";
+ monitor = ", preferred, auto, 1";
+ general = {
+ gaps_out = 10;
+ };
input = {
- kb_layout = "fi";
- repeat_delay = 280;
- repeat_rate = 100;
+ kb_layout = "us,fi";
+ kb_options = "grp:alt_shift_toggle";
+ scroll_method = "on_button_down";
+ scroll_button = 274;
+ sensitivity = 0; # -1.0 - 1.0, 0 means no modification
+ repeat_delay = 180;
+ repeat_rate = 150;
+ touchpad = {
+ natural_scroll = true;
+ disable_while_typing = true;
+ tap-to-click = true;
+ middle_button_emulation = true;
+ };
+ };
+ decoration = {
+ dim_inactive = true;
+ };
+ cursor = {
+ no_hardware_cursors = true;
+ };
+ misc = {
+ disable_hyprland_logo = true;
+ disable_splash_rendering = true;
+ middle_click_paste = true;
};
-
"$mainMod" = "SUPER";
bind = [
- "$mainMod, Return, exec, alacritty"
- "$mainMod, d, exec, wofi --show drun"
- "$mainMod, B, exec, firefox"
+ "$mainMod, V, exec, ${pkgs.ghostty}/bin/ghostty --class clipse -e 'clipse' "
+ "$mainMod, Return, exec, ${pkgs.ghostty}/bin/ghostty"
+ "$mainMod, d, exec, ${pkgs.walker}/bin/walker --modules applications,translation,webesearch,clipboard,finder"
+ "$mainMod, B, exec, ${pkgs.firefox}/bin/firefox"
"$mainMod, F, fullscreen"
"$mainMod, C, killactive,"
+ "$mainMod, Tab, cyclenext"
+ ", Print, exec, ${pkgs.grim}/bin/grim - | wl-copy && wl-paste > ~/Pictures/screenshots/Screenshot-$(date +%F_%T).png && dunstify 'Screenshot of the whole screen taken' -t 1000"
+ ", XF86MonBrightnessUp, exec, brightnessctl s -e 5%+"
+ ", XF86MonBrightnessDown, exec, brightnessctl s -e 5%-"
+ ", XF86AudioRaiseVolume,exec,wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
+ ", XF86AudioLowerVolume,exec,wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
+ "$mainMod SHIFT, L, exec, hyprlock"
# Move focus with mainMod + arrow keys
"$mainMod, left, movefocus, l"
@@ -30,6 +116,11 @@
"$mainMod, K, movefocus, u"
"$mainMod, J, movefocus, d"
+ "$mainMod SHIFT, left, swapwindow, l"
+ "$mainMod SHIFT, right, swapwindow, r"
+ "$mainMod SHIFT, up, swapwindow, u"
+ "$mainMod SHIFT, down, swapwindow, d"
+
# Switch workspaces with mainMod + [0-9]
"$mainMod, 1, workspace, 1"
"$mainMod, 2, workspace, 2"
@@ -54,6 +145,7 @@
"$mainMod SHIFT, 9, movetoworkspace, 9"
"$mainMod SHIFT, 0, movetoworkspace, 10"
];
+ binde = [ ",XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" ];
bindm = [
# Move/resize windows with mainMod + LMB/RMB and dragging
"$mainMod, mouse:272, movewindow"
@@ -61,6 +153,4 @@
];
};
};
-
- home.file.".config/hypr/hyprpaper.conf".source = ./hyprpaper.conf;
}
diff --git a/home/hyprland/hyprpaper.conf b/home/hyprland/hyprpaper.conf
deleted file mode 100644
index 67a04ed..0000000
--- a/home/hyprland/hyprpaper.conf
+++ /dev/null
@@ -1,2 +0,0 @@
-preload = ~/wallpapers/metsapalo.jpg
-wallpaper = eDP-1,~/wallpapers/metsapalo.jpg
diff --git a/home/hyprlock/default.nix b/home/hyprlock/default.nix
new file mode 100644
index 0000000..4627556
--- /dev/null
+++ b/home/hyprlock/default.nix
@@ -0,0 +1,72 @@
+{ vars, pkgs, ... }:
+
+{
+ programs.hyprlock = {
+ enable = true;
+ settings = {
+ general = {
+ grace = 300;
+ hide_cursor = true;
+ ignore_empty_input = true;
+ };
+
+ animations = {
+ enabled = true;
+ bezier = "linear, 1, 1, 0, 0";
+ animation = [
+ "fadeIn, 1, 5, linear"
+ "fadeOut, 1, 5, linear"
+ "inputFieldDots, 1, 2, linear"
+ ];
+ };
+
+ background = [
+ {
+ path = "screenshot";
+ blur_passes = 3;
+ blur_size = 8;
+ }
+ ];
+
+ input-field = [
+ {
+ size = "200, 50";
+ position = "0, -80";
+ monitor = "";
+ dots_center = true;
+ fade_on_empty = false;
+ font_color = "rgb(202, 211, 245)";
+ inner_color = "rgb(91, 96, 120)";
+ outer_color = "rgb(24, 25, 38)";
+ outline_thickness = 5;
+ shadow_passes = 2;
+ }
+ ];
+
+ label = [
+ # Clock with seconds
+ {
+ monitor = "";
+ text = "cmd[update:1000] echo \"$(date +'%H:%M:%S')\"";
+ color = "rgb(202, 211, 245)";
+ font_size = 90;
+ position = "0, 150";
+ halign = "center";
+ valign = "center";
+ shadow_passes = 2;
+ }
+ # Date in ISO format
+ {
+ monitor = "";
+ text = "cmd[update:1000] echo \"$(date +'%Y-%m-%d')\"";
+ color = "rgb(202, 211, 245)";
+ font_size = 25;
+ position = "0, 50";
+ halign = "center";
+ valign = "center";
+ shadow_passes = 2;
+ }
+ ];
+ };
+ };
+}
diff --git a/home/icons b/home/icons
new file mode 100644
index 0000000..4022691
--- /dev/null
+++ b/home/icons
@@ -0,0 +1,377 @@
+# vim:ft=conf
+
+# These examples require Nerd Fonts or a compatible font to be used.
+# See https://www.nerdfonts.com for more information.
+
+# default values from lf (with matching order)
+# ln l # LINK
+# or l # ORPHAN
+# tw t # STICKY_OTHER_WRITABLE
+# ow d # OTHER_WRITABLE
+# st t # STICKY
+# di d # DIR
+# pi p # FIFO
+# so s # SOCK
+# bd b # BLK
+# cd c # CHR
+# su u # SETUID
+# sg g # SETGID
+# ex x # EXEC
+# fi - # FILE
+
+# file types (with matching order)
+ln  # LINK
+or  # ORPHAN
+tw t # STICKY_OTHER_WRITABLE
+ow  # OTHER_WRITABLE
+st t # STICKY
+di  # DIR
+pi p # FIFO
+so s # SOCK
+bd b # BLK
+cd c # CHR
+su u # SETUID
+sg g # SETGID
+ex  # EXEC
+fi  # FILE
+
+# disable some default filetype icons, let them choose icon by filename
+# ln  # LINK
+# or  # ORPHAN
+# tw # STICKY_OTHER_WRITABLE
+# ow # OTHER_WRITABLE
+# st # STICKY
+# di  # DIR
+# pi # FIFO
+# so # SOCK
+# bd # BLK
+# cd # CHR
+# su # SETUID
+# sg # SETGID
+# ex # EXEC
+# fi  # FILE
+
+# file extensions (vim-devicons)
+*.styl 
+*.sass 
+*.scss 
+*.htm 
+*.html 
+*.slim 
+*.haml 
+*.ejs 
+*.css 
+*.less 
+*.md 
+*.mdx 
+*.markdown 
+*.rmd 
+*.json 
+*.webmanifest 
+*.js 
+*.mjs 
+*.jsx 
+*.rb 
+*.gemspec 
+*.rake 
+*.php 
+*.py 
+*.pyc 
+*.pyo 
+*.pyd 
+*.coffee 
+*.mustache 
+*.hbs 
+*.conf 
+*.ini 
+*.yml 
+*.yaml 
+*.toml 
+*.bat 
+*.mk 
+*.jpg 
+*.jpeg 
+*.bmp 
+*.png 
+*.webp 
+*.gif 
+*.ico 
+*.twig 
+*.cpp 
+*.c++ 
+*.cxx 
+*.cc 
+*.cp 
+*.c 
+*.cs 󰌛
+*.h 
+*.hh 
+*.hpp 
+*.hxx 
+*.hs 
+*.lhs 
+*.nix 
+*.lua 
+*.java 
+*.sh 
+*.fish 
+*.bash 
+*.zsh 
+*.ksh 
+*.csh 
+*.awk 
+*.ps1 
+*.ml λ
+*.mli λ
+*.diff 
+*.db 
+*.sql 
+*.dump 
+*.clj 
+*.cljc 
+*.cljs 
+*.edn 
+*.scala 
+*.go 
+*.dart 
+*.xul 
+*.sln 
+*.suo 
+*.pl 
+*.pm 
+*.t 
+*.rss 
+'*.f#' 
+*.fsscript 
+*.fsx 
+*.fs 
+*.fsi 
+*.rs 
+*.rlib 
+*.d 
+*.erl 
+*.hrl 
+*.ex 
+*.exs 
+*.eex 
+*.leex 
+*.heex 
+*.vim 
+*.ai 
+*.psd 
+*.psb 
+*.ts 
+*.tsx 
+*.jl 
+*.pp 
+*.vue 
+*.elm 
+*.swift 
+*.xcplayground 
+*.tex 󰙩
+*.r 󰟔
+*.rproj 󰗆
+*.sol 󰡪
+*.pem 
+
+# file names (vim-devicons) (case-insensitive not supported in lf)
+*gruntfile.coffee 
+*gruntfile.js 
+*gruntfile.ls 
+*gulpfile.coffee 
+*gulpfile.js 
+*gulpfile.ls 
+*mix.lock 
+*dropbox 
+*.ds_store 
+*.gitconfig 
+*.gitignore 
+*.gitattributes 
+*.gitlab-ci.yml 
+*.bashrc 
+*.zshrc 
+*.zshenv 
+*.zprofile 
+*.vimrc 
+*.gvimrc 
+*_vimrc 
+*_gvimrc 
+*.bashprofile 
+*favicon.ico 
+*license 
+*node_modules 
+*react.jsx 
+*procfile 
+*dockerfile 
+*docker-compose.yml 
+*docker-compose.yaml 
+*compose.yml 
+*compose.yaml 
+*rakefile 
+*config.ru 
+*gemfile 
+*makefile 
+*cmakelists.txt 
+*robots.txt 󰚩
+
+# file names (case-sensitive adaptations)
+*Gruntfile.coffee 
+*Gruntfile.js 
+*Gruntfile.ls 
+*Gulpfile.coffee 
+*Gulpfile.js 
+*Gulpfile.ls 
+*Dropbox 
+*.DS_Store 
+*LICENSE 
+*React.jsx 
+*Procfile 
+*Dockerfile 
+*Docker-compose.yml 
+*Docker-compose.yaml 
+*Rakefile 
+*Gemfile 
+*Makefile 
+*CMakeLists.txt 
+
+# file patterns (vim-devicons) (patterns not supported in lf)
+# .*jquery.*\.js$ 
+# .*angular.*\.js$ 
+# .*backbone.*\.js$ 
+# .*require.*\.js$ 
+# .*materialize.*\.js$ 
+# .*materialize.*\.css$ 
+# .*mootools.*\.js$ 
+# .*vimrc.* 
+# Vagrantfile$ 
+
+# file patterns (file name adaptations)
+*jquery.min.js 
+*angular.min.js 
+*backbone.min.js 
+*require.min.js 
+*materialize.min.js 
+*materialize.min.css 
+*mootools.min.js 
+*vimrc 
+Vagrantfile 
+
+# archives or compressed (extensions from dircolors defaults)
+*.tar 
+*.tgz 
+*.arc 
+*.arj 
+*.taz 
+*.lha 
+*.lz4 
+*.lzh 
+*.lzma 
+*.tlz 
+*.txz 
+*.tzo 
+*.t7z 
+*.zip 
+*.z 
+*.dz 
+*.gz 
+*.lrz 
+*.lz 
+*.lzo 
+*.xz 
+*.zst 
+*.tzst 
+*.bz2 
+*.bz 
+*.tbz 
+*.tbz2 
+*.tz 
+*.deb 
+*.rpm 
+*.jar 
+*.war 
+*.ear 
+*.sar 
+*.rar 
+*.alz 
+*.ace 
+*.zoo 
+*.cpio 
+*.7z 
+*.rz 
+*.cab 
+*.wim 
+*.swm 
+*.dwm 
+*.esd 
+
+# image formats (extensions from dircolors defaults)
+*.jpg 
+*.jpeg 
+*.mjpg 
+*.mjpeg 
+*.gif 
+*.bmp 
+*.pbm 
+*.pgm 
+*.ppm 
+*.tga 
+*.xbm 
+*.xpm 
+*.tif 
+*.tiff 
+*.png 
+*.svg 
+*.svgz 
+*.mng 
+*.pcx 
+*.mov 
+*.mpg 
+*.mpeg 
+*.m2v 
+*.mkv 
+*.webm 
+*.ogm 
+*.mp4 
+*.m4v 
+*.mp4v 
+*.vob 
+*.qt 
+*.nuv 
+*.wmv 
+*.asf 
+*.rm 
+*.rmvb 
+*.flc 
+*.avi 
+*.fli 
+*.flv 
+*.gl 
+*.dl 
+*.xcf 
+*.xwd 
+*.yuv 
+*.cgm 
+*.emf 
+*.ogv 
+*.ogx 
+
+# audio formats (extensions from dircolors defaults)
+*.aac 
+*.au 
+*.flac 
+*.m4a 
+*.mid 
+*.midi 
+*.mka 
+*.mp3 
+*.mpc 
+*.ogg 
+*.ra 
+*.wav 
+*.oga 
+*.opus 
+*.spx 
+*.xspf 
+
+# other formats
+*.pdf 
diff --git a/home/lf/default.nix b/home/lf/default.nix
new file mode 100644
index 0000000..dae7e7f
--- /dev/null
+++ b/home/lf/default.nix
@@ -0,0 +1,41 @@
+{ pkgs, ... }:
+{
+ programs.lf = {
+ enable = true;
+ commands = {
+ editor-open = ''$$EDITOR $f'';
+ };
+ settings = {
+ preview = true;
+ hidden = true;
+ drawbox = true;
+ icons = true;
+ ignorecase = true;
+ timefmt = "2006-01-02T15:04:05.999Z";
+ };
+ previewer.source = "/home/petri/.config/lf/pv.sh";
+ keybindings = {
+ ee = "editor-open";
+ };
+ };
+
+ xdg.configFile."/home/petri/.config/lf/icons".source = ./icons;
+ xdg.configFile."/home/petri/.config/lf/pv.sh" = {
+ executable = true;
+ text = ''
+ #!/bin/sh
+ case "$1" in
+ *.jpg*) timg -I "$1";;
+ *.png*) timg -I "$1";;
+ *.webp*) timg -I "$1";;
+ *.avif*) timg -I "$1";;
+ *.tar*) tar tf "$1";;
+ *.zip) unzip -l "$1";;
+ *.rar) unrar l "$1";;
+ *.7z) 7z l "$1";;
+ *.pdf) pdftotext "$1" -;;
+ *) pistol "$1";;
+ esac
+ '';
+ };
+}
diff --git a/home/lock/default.nix b/home/lock/default.nix
deleted file mode 100644
index 188d4b7..0000000
--- a/home/lock/default.nix
+++ /dev/null
@@ -1,62 +0,0 @@
-{ pkgs, ... }:
-
-{
- programs.swaylock = {
- enable = true;
-
- package = pkgs.swaylock-effects;
- settings = {
- ignore-empty-password = true;
- disable-caps-lock-text = true;
- # font = "MonaspiceAr Nerd Font";
- grace = 3;
-
- clock = true;
- timestr = "%R";
- datestr = "%a, %e of %B";
-
- fade-in = "0.2";
-
- effect-blur = "10x2";
- effect-scale = "0.1";
-
- indicator = true;
- indicator-radius = 240;
- indicator-thickness = 20;
- indicator-caps-lock = true;
-
- key-hl-color = "#8aadf4";
- bs-hl-color = "#ed8796";
- caps-lock-key-hl-color = "#f5a97f";
- caps-lock-bs-hl-color = "#ed8796";
-
- separator-color = "#181926";
-
- inside-color = "#24273a";
- inside-clear-color = "#24273a";
- inside-caps-lock-color = "#24273a";
- inside-ver-color = "#24273a";
- inside-wrong-color = "#24273a";
-
- ring-color = "#1e2030";
- ring-clear-color = "#8aadf4";
- ring-caps-lock-color = "231f20D9";
- ring-ver-color = "#1e2030";
- ring-wrong-color = "#ed8796";
-
- line-color = "#8aadf4";
- line-clear-color = "#8aadf4";
- line-caps-lock-color = "#f5a97f";
- line-ver-color = "#181926";
- line-wrong-color = "#ed8796";
-
- text-color = "#8aadf4";
- text-clear-color = "#24273a";
- text-caps-lock-color = "#f5a97f";
- text-ver-color = "#24273a";
- text-wrong-color = "#24273a";
-
- debug = true;
- };
- };
-}
diff --git a/home/mail/default.nix b/home/mail/default.nix
new file mode 100644
index 0000000..2b2b031
--- /dev/null
+++ b/home/mail/default.nix
@@ -0,0 +1,244 @@
+{ pkgs, config, ... }:
+
+let
+ # Common configuration for Gmail-based accounts
+ mkGmailAccount =
+ {
+ name,
+ address,
+ userName,
+ passwordCommand,
+ primary ? false,
+ }:
+ let
+ accountConfig = {
+ inherit
+ primary
+ address
+ userName
+ passwordCommand
+ ;
+ realName = "Petri Hienonen";
+
+ imap = {
+ host = "imap.gmail.com";
+ port = 993;
+ tls.enable = true;
+ };
+
+ smtp = {
+ host = "smtp.gmail.com";
+ port = 587;
+ tls.enable = true;
+ };
+
+ maildir = {
+ path = name;
+ };
+
+ mbsync = {
+ enable = true;
+ create = "both";
+ expunge = "both";
+ remove = "both";
+ patterns = [
+ "*"
+ "!Drafts"
+ "!Sent Mail"
+ ];
+ extraConfig.channel = {
+ CopyArrivalDate = "yes";
+ SyncState = "*";
+ };
+ };
+
+ notmuch.enable = true;
+
+ himalaya = {
+ enable = true;
+ settings = {
+ inherit primary;
+ display-name = "Petri Hienonen";
+ signature = "Regards,\nPetri Hienonen";
+ signature-delim = "-- \n";
+
+ "backend.type" = "notmuch";
+ "backend.db-path" = "${config.home.homeDirectory}/Mail/${name}";
+
+ "folder.aliases.inbox" = "INBOX";
+ "folder.aliases.sent" = "Sent Mail";
+ "folder.aliases.drafts" = "Drafts";
+ "folder.aliases.trash" = "Trash";
+
+ "message.write.headers" = [
+ "From"
+ "To"
+ "Cc"
+ "Bcc"
+ "Subject"
+ ];
+ "message.send.save-copy" = true;
+ "message.delete.style" = "folder";
+
+ "template.new.signature-style" = "inlined";
+ "template.reply.posting-style" = "top";
+ "template.reply.signature-style" = "below-quote";
+ "template.reply.quote-headline-fmt" = "On %d/%m/%Y %H:%M, {senders} wrote:\n";
+
+ "message.send.backend.type" = "smtp";
+ "message.send.backend.host" = "smtp.gmail.com";
+ "message.send.backend.port" = 465;
+ "message.send.backend.login" = userName;
+ "message.send.backend.auth.type" = "password";
+ "message.send.backend.auth.cmd" = passwordCommand;
+
+ "pgp.type" = "commands";
+ "pgp.encrypt-cmd" = "${pkgs.gnupg}/bin/gpg --encrypt --armor --quiet -r '%r'";
+ "pgp.decrypt-cmd" = "${pkgs.gnupg}/bin/gpg --decrypt --quiet";
+ "pgp.sign-cmd" = "${pkgs.gnupg}/bin/gpg --sign --armor --quiet --default-key '%s'";
+ "pgp.verify-cmd" = "${pkgs.gnupg}/bin/gpg --verify --quiet";
+ };
+ };
+
+ aerc = {
+ enable = true;
+ };
+
+ imapnotify = {
+ enable = true;
+ boxes = [ "INBOX" ];
+ onNotify = emailNotifyScript name;
+ extraConfig.wait = 60;
+ };
+ };
+ in
+ accountConfig;
+
+ # Simplified notification script that only extracts sender and subject
+ emailNotifyScript =
+ account:
+ pkgs.writeShellScript "email-notify-${account}" ''
+ set -euo pipefail
+
+ # Sync this account specifically
+ ${pkgs.isync}/bin/mbsync "${account}"
+
+ # Wait a moment for notmuch to index
+ sleep 2
+ ${pkgs.notmuch}/bin/notmuch new
+
+ LATEST_MSG=$(${pkgs.notmuch}/bin/notmuch search --output=messages --limit=1 "tag:unread and folder:${account}/INBOX" | head -1)
+
+ if [ -n "$LATEST_MSG" ]; then
+ # Extract sender and subject using notmuch show
+ SENDER=$(${pkgs.notmuch}/bin/notmuch show --format=json "$LATEST_MSG" | ${pkgs.jq}/bin/jq -r '.[][0].headers.From // "Unknown sender"')
+ SUBJECT=$(${pkgs.notmuch}/bin/notmuch show --format=json "$LATEST_MSG" | ${pkgs.jq}/bin/jq -r '.[][0].headers.Subject // "No subject"')
+
+ # Send notification
+ ${pkgs.libnotify}/bin/notify-send -i mail-unread -a "Email" \
+ "New email in ${account}" \
+ "From: $SENDER\nSubject: $SUBJECT" \
+ -t 10000 # 10 second timeout
+ fi
+ '';
+
+in
+{
+ accounts.email = {
+ maildirBasePath = "${config.home.homeDirectory}/Mail";
+
+ accounts = {
+ gmail = mkGmailAccount {
+ name = "gmail";
+ primary = true;
+ address = "petri.hienonen@gmail.com";
+ userName = "petri.hienonen@gmail.com";
+ passwordCommand = "${pkgs.coreutils}/bin/cat ${config.age.secrets.gmail.path}";
+ };
+
+ relesoft = mkGmailAccount {
+ name = "relesoft";
+ primary = false;
+ address = "petri.hienonen@relesoft.io";
+ userName = "petri.hienonen@relesoft.io";
+ passwordCommand = "${pkgs.coreutils}/bin/cat ${config.age.secrets.relesoft.path}";
+ };
+ };
+ };
+
+ # Global settings for programs (not account-specific)
+ programs.mbsync.enable = true;
+
+ programs.notmuch = {
+ enable = true;
+ hooks = {
+ preNew = "${pkgs.isync}/bin/mbsync -a || true";
+ };
+ new = {
+ tags = [
+ "new"
+ "unread"
+ ];
+ ignore = [
+ "/.*[.](json|lock|bak)$/"
+ ".uidvalidity"
+ ".mbsyncstate"
+ ".mbsyncstate.journal"
+ ".msoepln"
+ ".mbsyncstate.new"
+ ".mbsyncstate.lock"
+ ];
+ };
+ };
+
+ programs.himalaya = {
+ enable = true;
+ package = pkgs.himalaya.override {
+ withFeatures = [
+ "notmuch"
+ "pgp-gpg"
+ ];
+ };
+ };
+
+ # aerc configuration
+ programs.aerc = {
+ enable = true;
+ # aerc will automatically pick up accounts from accounts.email
+ # Additional aerc-wide configuration can go here
+ extraConfig = {
+ general = {
+ unsafe-accounts-conf = true;
+ # Example aerc configuration
+ # ui.message-list-time-format = "2006-01-02 15:04"
+ # ui.timestamp-format = "2006-01-02 15:04"
+ };
+ };
+ };
+
+ systemd.user.services.mbsync = {
+ Unit = {
+ Description = "Sync mail with mbsync";
+ After = [ "network-online.target" ];
+ Wants = [ "network-online.target" ];
+ };
+ Service = {
+ Type = "oneshot";
+ ExecStart = "${pkgs.isync}/bin/mbsync -a";
+ };
+ Install.WantedBy = [ "default.target" ];
+ };
+
+ systemd.user.timers.mbsync = {
+ Unit = {
+ Description = "Timer for mbsync mail synchronization";
+ Requires = "mbsync.service";
+ };
+ Timer = {
+ OnBootSec = "5m";
+ OnUnitInactiveSec = "5m";
+ Unit = "mbsync.service";
+ };
+ Install.WantedBy = [ "timers.target" ];
+ };
+}
diff --git a/home/mpv/default.nix b/home/mpv/default.nix
new file mode 100644
index 0000000..4cdb3e9
--- /dev/null
+++ b/home/mpv/default.nix
@@ -0,0 +1,25 @@
+{ inputs, pkgs, ... }:
+{
+ programs.mpv = {
+ enable = true;
+
+ package = (
+ pkgs.mpv-unwrapped.wrapper {
+ scripts = with pkgs.mpvScripts; [
+ uosc
+ sponsorblock
+ ];
+
+ mpv = pkgs.mpv-unwrapped.override { waylandSupport = true; };
+ }
+ );
+
+ config = {
+ profile = "high-quality";
+ ytdl-format = "bestvideo+bestaudio";
+ cache = "yes";
+ demuxer-max-bytes = "512MiB";
+ demuxer-readahead-secs = "20";
+ };
+ };
+}
diff --git a/home/newsboat/default.nix b/home/newsboat/default.nix
new file mode 100644
index 0000000..c95b95a
--- /dev/null
+++ b/home/newsboat/default.nix
@@ -0,0 +1,59 @@
+{
+ vars,
+ config,
+ pkgs,
+ ...
+}:
+
+{
+ xdg.configFile."/home/petri/.config/newsboat/urls".text = ''
+ "query:Unread Articles:unread = \"yes\""
+ '';
+
+ programs.newsboat = {
+ enable = true;
+ autoReload = true;
+ browser = "\"${pkgs.chawan}/bin/cha %u\"";
+ reloadTime = 2;
+ extraConfig = ''
+ # special
+ macro o set browser "rdrview -T title,body -B cha %u" ; open-in-browser ; set browser "cha %u"
+
+ # download
+ prepopulate-query-feeds yes
+ download-full-page yes
+ urls-source "miniflux"
+ miniflux-url "https://flux.tammi.cc"
+ miniflux-tokenfile ${config.age.secrets.miniflux_api_key.path}
+
+ # operation
+ goto-next-feed no
+ miniflux-show-special-feeds "no"
+ feed-sort-order unreadarticlecount-asc
+ article-sort-order date-asc
+
+ # keys
+ bind-key j down
+ bind-key k up
+ bind-key j next articlelist
+ bind-key k prev articlelist
+ bind-key J next-feed articlelist
+ bind-key K prev-feed articlelist
+ bind-key G end
+ bind-key g home
+ bind-key d pagedown
+ bind-key u pageup
+ bind-key l open
+ bind-key h quit
+ bind-key a toggle-article-read
+ bind-key n next-unread
+ bind-key N prev-unread
+ bind-key D pb-download
+ bind-key U show-urls
+ bind-key x pb-delete
+ bind-key o open-in-browser
+
+ include ${pkgs.newsboat}/share/doc/newsboat/contrib/colorschemes/solarized-dark
+ '';
+ };
+}
diff --git a/home/nvim/autocommands.lua b/home/nvim/autocommands.lua
new file mode 100644
index 0000000..b8bf173
--- /dev/null
+++ b/home/nvim/autocommands.lua
@@ -0,0 +1,272 @@
+local augroup = vim.api.nvim_create_augroup("UserConfig", {})
+
+vim.api.nvim_create_autocmd("TextYankPost", {
+ desc = "Highlight when yanking (copying) text",
+ group = vim.api.nvim_create_augroup("kickstart-highlight-yank", { clear = true }),
+ callback = function() vim.highlight.on_yank() end,
+})
+
+vim.api.nvim_create_autocmd({ "BufWritePre" }, {
+ pattern = { "*" },
+ command = [[%s/\s\+$//e]],
+})
+
+-- Return to last edit position when opening files
+vim.api.nvim_create_autocmd("BufReadPost", {
+ group = augroup,
+ callback = function()
+ local mark = vim.api.nvim_buf_get_mark(0, '"')
+ local lcount = vim.api.nvim_buf_line_count(0)
+ if mark[1] > 0 and mark[1] <= lcount then pcall(vim.api.nvim_win_set_cursor, 0, mark) end
+ end,
+})
+
+-- Auto-close terminal when process exits
+vim.api.nvim_create_autocmd("TermClose", {
+ group = augroup,
+ callback = function()
+ if vim.v.event.status == 0 then vim.api.nvim_buf_delete(0, {}) end
+ end,
+})
+
+-- Disable line numbers in terminal
+vim.api.nvim_create_autocmd("TermOpen", {
+ group = augroup,
+ callback = function()
+ vim.opt_local.number = false
+ vim.opt_local.relativenumber = false
+ vim.opt_local.signcolumn = "no"
+ end,
+})
+
+-- Auto-resize splits when window is resized
+vim.api.nvim_create_autocmd("VimResized", {
+ group = augroup,
+ callback = function() vim.cmd("tabdo wincmd =") end,
+})
+
+local og_virt_text
+local og_virt_line
+
+vim.api.nvim_create_autocmd({ "CursorMoved", "DiagnosticChanged" }, {
+ group = vim.api.nvim_create_augroup("diagnostic_only_virtlines", { clear = true }),
+ callback = function()
+ if og_virt_line == nil then og_virt_line = vim.diagnostic.config().virtual_lines end
+
+ -- ignore if virtual_lines.current_line is disabled
+ if not (og_virt_line and og_virt_line.current_line) then
+ if og_virt_text then
+ vim.diagnostic.config({ virtual_text = og_virt_text })
+ og_virt_text = nil
+ end
+ return
+ end
+
+ if og_virt_text == nil then og_virt_text = vim.diagnostic.config().virtual_text end
+
+ local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1
+
+ if vim.tbl_isempty(vim.diagnostic.get(0, { lnum = lnum })) then
+ vim.diagnostic.config({ virtual_text = og_virt_text })
+ else
+ vim.diagnostic.config({ virtual_text = false })
+ end
+ end,
+})
+
+vim.api.nvim_create_autocmd("LspAttach", {
+ group = vim.api.nvim_create_augroup("lsp-attach", { clear = true }),
+ callback = function(event)
+ local map = function(keys, func, desc)
+ vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc })
+ end
+
+ local client = vim.lsp.get_client_by_id(event.data.client_id)
+
+ if client and client:supports_method("textDocument/implementation") then
+ -- defaults:
+ -- https://neovim.io/doc/user/news-0.11.html#_defaults
+ map("gl", vim.diagnostic.open_float, "Open Diagnostic Float")
+ map("K", vim.lsp.buf.hover, "Hover Documentation")
+ map("gs", vim.lsp.buf.signature_help, "Signature Documentation")
+ map("gD", vim.lsp.buf.declaration, "Goto Declaration")
+ map("<leader>la", vim.lsp.buf.code_action, "Code Action")
+ map("<leader>lr", vim.lsp.buf.rename, "Rename all references")
+ map("<leader>lf", vim.lsp.buf.format, "Format")
+ map(
+ "<leader>v",
+ "<cmd>vsplit | lua vim.lsp.buf.definition()<cr>",
+ "Goto Definition in Vertical Split"
+ )
+ map("gd", vim.lsp.buf.definition, "Goto Definition")
+ map("gr", vim.lsp.buf.references, "Goto References")
+ map("gI", vim.lsp.buf.implementation, "Goto Implementation")
+ end
+
+ if client and client:supports_method("textDocument/completion") then
+ vim.lsp.completion.enable(true, client.id, event.buf, {
+ autotrigger = true,
+ convert = function(item) return { abbr = item.label:gsub("%b()", "") } end,
+ })
+ vim.keymap.set("i", "<c-space>", function() vim.lsp.completion.get() end)
+ client.server_capabilities.completionProvider = client.server_capabilities.completionProvider or {}
+ local trigger_chars = {}
+ for i = 32, 126 do
+ table.insert(trigger_chars, string.char(i))
+ end
+ client.server_capabilities.completionProvider.triggerCharacters = vim.list_extend(
+ client.server_capabilities.completionProvider.triggerCharacters or {},
+ trigger_chars
+ )
+ end
+
+ if client and client:supports_method("textDocument/formatting") then
+ vim.api.nvim_create_autocmd("BufWritePre", {
+ group = vim.api.nvim_create_augroup("my.lsp", { clear = false }),
+ buffer = event.buf,
+ callback = function()
+ vim.lsp.buf.format({ bufnr = event.buf, id = client.id, timeout_ms = 1000 })
+ end,
+ })
+ end
+
+ if client and client:supports_method("textDocument/inlayHint") then
+ vim.lsp.inlay_hint.enable(true, { bufnr = event.buf })
+ end
+
+ if client and client:supports_method("textDocument/documentHighlight") then
+ local highlight_augroup =
+ vim.api.nvim_create_augroup("lsp-highlight", { clear = false })
+
+ -- When cursor stops moving: Highlights all instances of the symbol under the cursor
+ -- When cursor moves: Clears the highlighting
+ vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
+ buffer = event.buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.document_highlight,
+ })
+ vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
+ buffer = event.buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.clear_references,
+ })
+
+ -- When LSP detaches: Clears the highlighting
+ vim.api.nvim_create_autocmd("LspDetach", {
+ group = vim.api.nvim_create_augroup("lsp-detach", { clear = true }),
+ callback = function(event2)
+ vim.lsp.buf.clear_references()
+ vim.api.nvim_clear_autocmds({ group = "lsp-highlight", buffer = event2.buf })
+ end,
+ })
+ end
+ end,
+})
+
+vim.api.nvim_create_autocmd("ModeChanged", {
+ group = vim.api.nvim_create_augroup("diagnostic_redraw", {}),
+ callback = function() pcall(vim.diagnostic.show) end,
+})
+
+-- terminal
+local terminal_state = {
+ buf = nil,
+ win = nil,
+ is_open = false,
+}
+
+local function FloatingTerminal()
+ -- If terminal is already open, close it (toggle behavior)
+ if terminal_state.is_open and vim.api.nvim_win_is_valid(terminal_state.win) then
+ vim.api.nvim_win_close(terminal_state.win, false)
+ terminal_state.is_open = false
+ return
+ end
+
+ -- Create buffer if it doesn't exist or is invalid
+ if not terminal_state.buf or not vim.api.nvim_buf_is_valid(terminal_state.buf) then
+ terminal_state.buf = vim.api.nvim_create_buf(false, true)
+ -- Set buffer options for better terminal experience
+ vim.api.nvim_buf_set_option(terminal_state.buf, "bufhidden", "hide")
+ end
+
+ -- Calculate window dimensions
+ local width = math.floor(vim.o.columns * 0.8)
+ local height = math.floor(vim.o.lines * 0.8)
+ local row = math.floor((vim.o.lines - height) / 2)
+ local col = math.floor((vim.o.columns - width) / 2)
+
+ -- Create the floating window
+ terminal_state.win = vim.api.nvim_open_win(terminal_state.buf, true, {
+ relative = "editor",
+ width = width,
+ height = height,
+ row = row,
+ col = col,
+ style = "minimal",
+ border = "rounded",
+ })
+
+ -- Set transparency for the floating window
+ vim.api.nvim_win_set_option(terminal_state.win, "winblend", 0)
+
+ -- Set transparent background for the window
+ vim.api.nvim_win_set_option(
+ terminal_state.win,
+ "winhighlight",
+ "Normal:FloatingTermNormal,FloatBorder:FloatingTermBorder"
+ )
+
+ -- Define highlight groups for transparency
+ vim.api.nvim_set_hl(0, "FloatingTermNormal", { bg = "none" })
+ vim.api.nvim_set_hl(0, "FloatingTermBorder", { bg = "none" })
+
+ -- Start terminal if not already running
+ local has_terminal = false
+ local lines = vim.api.nvim_buf_get_lines(terminal_state.buf, 0, -1, false)
+ for _, line in ipairs(lines) do
+ if line ~= "" then
+ has_terminal = true
+ break
+ end
+ end
+
+ if not has_terminal then vim.fn.termopen(os.getenv("SHELL")) end
+
+ terminal_state.is_open = true
+ vim.cmd("startinsert")
+
+ -- Set up auto-close on buffer leave
+ vim.api.nvim_create_autocmd("BufLeave", {
+ buffer = terminal_state.buf,
+ callback = function()
+ if terminal_state.is_open and vim.api.nvim_win_is_valid(terminal_state.win) then
+ vim.api.nvim_win_close(terminal_state.win, false)
+ terminal_state.is_open = false
+ end
+ end,
+ once = true,
+ })
+end
+
+-- Function to explicitly close the terminal
+local function CloseFloatingTerminal()
+ if terminal_state.is_open and vim.api.nvim_win_is_valid(terminal_state.win) then
+ vim.api.nvim_win_close(terminal_state.win, false)
+ terminal_state.is_open = false
+ end
+end
+
+-- Key mappings
+vim.keymap.set(
+ "n",
+ "<leader>t",
+ FloatingTerminal,
+ { noremap = true, silent = true, desc = "Toggle floating terminal" }
+)
+vim.keymap.set("t", "<Esc>", function()
+ if terminal_state.is_open then
+ vim.api.nvim_win_close(terminal_state.win, false)
+ terminal_state.is_open = false
+ end
+end, { noremap = true, silent = true, desc = "Close floating terminal from terminal mode" })
diff --git a/home/nvim/default.nix b/home/nvim/default.nix
new file mode 100644
index 0000000..038d8d6
--- /dev/null
+++ b/home/nvim/default.nix
@@ -0,0 +1,93 @@
+{ pkgs, pkgs-unstable, ... }:
+
+{
+ programs.neovim =
+ let
+ toLua = str: "lua << EOF\n${str}\nEOF\n";
+ toLuaFile = file: "lua << EOF\n${builtins.readFile file}\nEOF\n";
+ in
+ {
+ enable = true;
+ defaultEditor = true;
+ viAlias = true;
+ vimAlias = true;
+ vimdiffAlias = true;
+ extraPackages = with pkgs-unstable; [
+ inotify-tools
+
+ # debuggers
+ delve # golang debugger
+ lldb # rust, c, etc, debugger
+
+ # format
+ rustfmt
+ dprint # platform for many formatters
+ yamlfmt
+
+ # LSP
+ biome # javascript, biome
+ clippy # rust error checking
+ clang-tools # C
+ dprint # format engine for multiple langeuages
+ dprint-plugins.dprint-plugin-markdown # markdown
+ dprint-plugins.dprint-plugin-toml # toml
+ dprint-plugins.g-plane-malva # css
+ dprint-plugins.g-plane-markup_fmt # html
+ dprint-plugins.g-plane-pretty_yaml # yaml
+ fish-lsp # fish
+ gopls # golang
+ ltex-ls # latex, markdown
+ lua-language-server # lua
+ nil # lsp server for nix
+ nodePackages.bash-language-server # bash
+ nodePackages.typescript-language-server # javascript validation
+ ruff # python format and lint
+ rust-analyzer
+ tex-fmt # latex
+ texlab # latex lsp
+ tree-sitter # generate tree-sitter grammars
+ ty # python type checker written in rust
+ vale-ls # prose (md, asciidoc)
+ ];
+
+ extraPython3Packages = ps: [
+ ps.debugpy
+ ps.pynvim
+ ];
+ plugins = with pkgs-unstable.vimPlugins; [
+ {
+ plugin = nvim-dap;
+ config = toLuaFile ./plugins/dap.lua;
+ }
+ {
+ plugin = which-key-nvim;
+ config = toLuaFile ./plugins/which.lua;
+ }
+ {
+ plugin = undotree;
+ config = toLuaFile ./plugins/undotree.lua;
+ }
+ {
+ plugin = mini-nvim;
+ config = toLuaFile ./plugins/mini.lua;
+ }
+ {
+ plugin = nvim-treesitter.withAllGrammars;
+ config = toLuaFile ./plugins/treesitter.lua;
+ }
+ hardtime-nvim
+ nvim-dap-view
+ nvim-dap-virtual-text
+ nord-nvim
+ ];
+
+ extraLuaConfig = ''
+ ${builtins.readFile ./lsp.lua}
+ ${builtins.readFile ./autocommands.lua}
+ ${builtins.readFile ./keymaps.lua}
+ ${builtins.readFile ./options.lua}
+ ${builtins.readFile ./plugins/other.lua}
+ '';
+ };
+
+}
diff --git a/home/nvim/keymaps.lua b/home/nvim/keymaps.lua
new file mode 100644
index 0000000..d8907bf
--- /dev/null
+++ b/home/nvim/keymaps.lua
@@ -0,0 +1,86 @@
+vim.g.mapleader = " "
+vim.g.maplocalleader = " "
+vim.keymap.set("v", "<", "<gv", { desc = "Indent left" })
+vim.keymap.set("v", ">", ">gv", { desc = "Indent right" })
+
+vim.keymap.set("n", "<leader><space>", "<cmd>Pick buffers<cr>", { desc = "Search open files" })
+vim.keymap.set("n", "<leader>ff", "<cmd>Pick files<cr>", { desc = "Search all files" })
+vim.keymap.set("n", "<leader>fh", "<cmd>Pick help<cr>", { desc = "Search help tags" })
+
+-- Alternative navigation (more intuitive)
+vim.keymap.set("n", "<leader>tn", ":tabnew<CR>", { desc = "New tab" })
+vim.keymap.set("n", "<leader>tx", ":tabclose<CR>", { desc = "Close tab" })
+
+-- Tab moving
+vim.keymap.set("n", "<leader>tm", ":tabmove<CR>", { desc = "Move tab" })
+vim.keymap.set("n", "<leader>t>", ":tabmove +1<CR>", { desc = "Move tab right" })
+vim.keymap.set("n", "<leader>t<", ":tabmove -1<CR>", { desc = "Move tab left" })
+
+vim.keymap.set("n", "<leader>dq", vim.diagnostic.disable)
+vim.keymap.set("n", "<leader>ds", vim.diagnostic.enable)
+
+vim.keymap.set({ "n", "x" }, "gy", '"+y', { desc = "Copy to clipboard" })
+vim.keymap.set({ "n", "x" }, "gp", '"+p', { desc = "Paste clipboard text" })
+
+vim.keymap.set("n", "<Esc>", "<cmd>nohlsearch<CR>")
+vim.keymap.set(
+ "n",
+ "<leader>q",
+ vim.diagnostic.setloclist,
+ { desc = "Open diagnostic [Q]uickfix list" }
+)
+vim.keymap.set("n", "<leader>q", ":bp<bar>sp<bar>bn<bar>bd<CR>", { desc = "Close buffer" })
+vim.keymap.set("n", "<leader>Q", ":bd!<CR>", { desc = "Force close buffer" })
+vim.keymap.set("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
+
+vim.keymap.set("n", "<C-h>", "<C-w><C-h>", { desc = "Move focus to the left window" })
+vim.keymap.set("n", "<C-l>", "<C-w><C-l>", { desc = "Move focus to the right window" })
+vim.keymap.set("n", "<C-j>", "<C-w><C-j>", { desc = "Move focus to the lower window" })
+vim.keymap.set("n", "<C-k>", "<C-w><C-k>", { desc = "Move focus to the upper window" })
+
+vim.keymap.set("n", "<leader>e", "<CMD>:lua MiniFiles.open()<CR>", { desc = "Open [E]xplorer" })
+
+vim.keymap.set("n", "<F5>", "<cmd>DapContinue<CR>", { desc = "DAP: Continue" })
+vim.keymap.set("n", "<leader>bp", "<cmd>DapToggleBreakpoint<CR>", { desc = "DAP: Toggle Breakpoint" })
+vim.keymap.set("n", "<F6>", "<cmd>DapStepOver<CR>", { desc = "DAP: Step Over" })
+vim.keymap.set("n", "<F7>", "<cmd>DapStepInto<CR>", { desc = "DAP: Step Into" })
+vim.keymap.set("n", "<F8>", "<cmd>DapStepOut<CR>", { desc = "DAP: Step Out" })
+vim.keymap.set("n", "<leader>dt", "<cmd>DapTerminate<CR>", { desc = "DAP: Terminate" })
+
+local function tab_complete()
+ if vim.fn.pumvisible() == 1 then
+ -- navigate to next item in completion menu
+ return "<Down>"
+ end
+
+ local c = vim.fn.col(".") - 1
+ local is_whitespace = c == 0 or vim.fn.getline("."):sub(c, c):match("%s")
+
+ if is_whitespace then
+ -- insert tab
+ return "<Tab>"
+ end
+
+ local lsp_completion = vim.bo.omnifunc == "v:lua.vim.lsp.omnifunc"
+
+ if lsp_completion then
+ -- trigger lsp code completion
+ return "<C-x><C-o>"
+ end
+
+ -- suggest words in current buffer
+ return "<C-x><C-n>"
+end
+
+local function tab_prev()
+ if vim.fn.pumvisible() == 1 then
+ -- navigate to previous item in completion menu
+ return "<Up>"
+ end
+
+ -- insert tab
+ return "<Tab>"
+end
+
+vim.keymap.set("i", "<Tab>", tab_complete, { expr = true })
+vim.keymap.set("i", "<S-Tab>", tab_prev, { expr = true })
diff --git a/home/nvim/lsp.lua b/home/nvim/lsp.lua
new file mode 100644
index 0000000..07665bc
--- /dev/null
+++ b/home/nvim/lsp.lua
@@ -0,0 +1,305 @@
+-- example configurations available https://github.com/neovim/nvim-lspconfig/tree/master/lsp
+vim.lsp.set_log_level(vim.log.levels.WARN)
+vim.lsp.log.set_format_func(vim.inspect)
+
+vim.diagnostic.config({
+ virtual_text = true,
+ virtual_lines = { current_line = true },
+ underline = true,
+ update_in_insert = false,
+ severity_sort = true,
+ float = {
+ border = "rounded",
+ source = true,
+ },
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = "󰅚 ",
+ [vim.diagnostic.severity.WARN] = "󰀪 ",
+ [vim.diagnostic.severity.INFO] = "󰋽 ",
+ [vim.diagnostic.severity.HINT] = "󰌶 ",
+ },
+ numhl = {
+ [vim.diagnostic.severity.ERROR] = "ErrorMsg",
+ [vim.diagnostic.severity.WARN] = "WarningMsg",
+ },
+ },
+})
+
+local capabilities = vim.lsp.protocol.make_client_capabilities()
+
+capabilities = vim.tbl_deep_extend("force", capabilities, {
+ textDocument = {
+ inlayHint = {
+ dynamicRegistration = false, -- Static registration
+ resolveSupport = {
+ properties = { "textEdits", "tooltip", "label" }, -- Resolve additional hint details
+ },
+ },
+ synchronization = {
+ dynamicRegistration = false, -- Static registration
+ willSave = true, -- Notify server before saving
+ willSaveWaitUntil = true, -- Allow server to provide edits before saving
+ didSave = true, -- Notify server after saving
+ },
+ hover = {
+ dynamicRegistration = false, -- Static registration
+ contentFormat = { "markdown", "plaintext" }, -- Prefer markdown, fallback to plaintext
+ },
+ documentSymbol = {
+ dynamicRegistration = false, -- Static registration
+ hierarchicalDocumentSymbolSupport = true, -- Support nested symbols
+ symbolKind = {
+ valueSet = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ }, -- All standard symbol kinds (file, module, namespace, etc.)
+ },
+ },
+ completion = {
+ completionItem = {
+ snippetSupport = true,
+ preselectSupport = true,
+ insertReplaceSupport = true,
+ labelDetailsSupport = true,
+ deprecatedSupport = true,
+ commitCharactersSupport = true,
+ tagSupport = { valueSet = { 1 } },
+ resolveSupport = {
+ properties = { "documentation", "detail", "additionalTextEdits" },
+ },
+ },
+ },
+ diagnostic = {
+ documentDiagnosticProvider = true, -- Enable document-level diagnostics
+ relatedInformation = true, -- Show related diagnostic information
+ tagSupport = { valueSet = { 1, 2 } }, -- Support deprecated (1) and unused (2) tags
+ dataSupport = true, -- Allow custom data in diagnostics
+ },
+ semanticTokens = {
+ multilineTokenSupport = true,
+ overlappingTokenSupport = true,
+ augmentsSyntaxTokens = true,
+ },
+ foldingRange = {
+ dynamicRegistration = false,
+ lineFoldingOnly = true,
+ },
+ },
+ workspace = {
+ configuration = true,
+ workspaceFolders = true,
+ didChangeWatchedFiles = {
+ dynamicRegistration = true,
+ },
+ fileOperations = {
+ didRename = true,
+ willRename = true,
+ didDelete = true,
+ didCreate = true,
+ },
+ symbol = {
+ dynamicRegistration = false, -- Static registration
+ symbolKind = {
+ valueSet = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ }, -- All standard symbol kinds
+ },
+ },
+ diagnostic = {
+ workspaceDiagnosticsProvider = true, -- Enable workspace-level diagnostics
+ },
+ },
+})
+
+vim.lsp.config("*", {
+ capabilities = capabilities,
+ root_markers = { ".git" },
+})
+
+vim.lsp.config("lua_ls", {
+ cmd = { "lua-language-server" },
+ filetypes = { "lua" },
+ root_markers = { ".luarc.json", ".luarc.jsonc" },
+ settings = {
+ Lua = {
+ format = {
+ enable = true,
+ defaultConfig = {
+ indent_style = "tab",
+ indent_size = 1,
+ quote_style = "double",
+ max_line_length = 120
+ },
+ },
+ runtime = { version = "LuaJIT" },
+ diagnostics = { globals = { "vim", "luassert" }, enable = true },
+ workspace = {
+ checkThirdParty = true,
+ library = vim.api.nvim_get_runtime_file("", true),
+ },
+ telemetry = { enable = false },
+ hint = {
+ enable = true,
+ setType = true,
+ paramType = true,
+ paramName = "All",
+ },
+ },
+ },
+})
+
+vim.lsp.config("nil_ls", {
+ cmd = { "nil" },
+ root_markers = { "flake.nix" },
+ filetypes = { "nix" },
+ settings = {
+ ["nil"] = {
+ formatting = { command = { "nixfmt" } },
+ },
+ },
+})
+
+vim.lsp.config("ts_ls", {
+ cmd = { "typescript-language-server", "--stdio" },
+ root_markers = { ".editorconfig", "jsconfig.json" },
+ filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact", "vue" },
+ init_options = {
+ preferences = {
+ disableSuggestions = false,
+ includeCompletionsForModuleExports = true,
+ },
+ },
+ capabilities = {
+ textDocument = {
+ formatting = false,
+ },
+ },
+ on_attach = function(client, bufnr)
+ client.server_capabilities.documentFormattingProvider = false
+ client.server_capabilities.documentRangeFormattingProvider = false
+ end,
+})
+
+vim.lsp.config("ruff", {
+ cmd = { "ruff", "server" },
+ root_markers = { "pyproject.toml" },
+ filetypes = { "python" },
+ init_options = {
+ settings = {
+ configurationPreference = "filesystemFirst",
+ fixAll = true,
+ lineLength = 100,
+ lint = { enable = true },
+ organizeImports = true,
+ },
+ },
+})
+
+vim.lsp.config("dprint", {
+ cmd = { "dprint", "lsp" },
+ filetypes = { "toml", "yaml", "markdown", "css" },
+ settings = {},
+})
+
+vim.lsp.config("clangd", {
+ cmd = { "clangd" },
+ filetypes = { "c", "cpp" },
+ root_markers = {
+ ".clangd", ".git"
+ },
+})
+
+vim.lsp.config("vale_ls", {
+ cmd = { "vale-ls" },
+ filetypes = { "markdown", "text", "tex", "rst", "adoc", "asciidoc" },
+ root_markers = { ".vale.ini" },
+})
+
+vim.lsp.config("ty", {
+ cmd = { "ty", "server" },
+ root_markers = { "pyproject.toml" },
+ filetypes = { "python" },
+ settings = {
+ ty = {
+ diagnosticMode = "workspace",
+ experimental = {
+ rename = true,
+ autoImport = true,
+ completions = true,
+ },
+ },
+ },
+})
+
+vim.lsp.config("rust_analyzer", {
+ cmd = { "rust-analyzer" },
+ root_markers = { "Cargo.toml" },
+ filetypes = { "rust" },
+ settings = {
+ ["rust-analyzer"] = {
+ check = {
+ command = "clippy",
+ },
+ },
+ },
+})
+
+vim.lsp.config("biome", {
+ cmd = { "biome", "lsp-proxy" },
+ workspace_required = true,
+ filetypes = {
+ "graphql",
+ "javascript",
+ "json",
+ "html"
+ },
+ root_markers = { "biome.json" },
+ capabilities = {
+ textDocument = {
+ formatting = {
+ dynamicRegistration = false,
+ },
+ },
+ },
+ single_file_support = false
+})
+
+vim.lsp.config("gopls", {
+ cmd = { "gopls" },
+ root_markers = { "go.mod" },
+ filetypes = { "go", "gomod", "gowork", "gotmpl" },
+ settings = {
+ gopls = {
+ analyses = {
+ unusedparams = true,
+ },
+ staticcheck = true,
+ },
+ },
+})
+
+vim.lsp.config("bashls", {
+ cmd = { "bash-language-server", "start" },
+ filetypes = { "sh" },
+})
+
+vim.lsp.config("texlab", {
+ cmd = { "texlab" },
+ filetypes = { "tex", "plaintex", "bib" },
+})
+
+vim.lsp.enable({
+ "bashls",
+ "biome",
+ "clangd",
+ "dprint",
+ "gopls",
+ "lua_ls",
+ "nil_ls",
+ "ruff",
+ "rust_analyzer",
+ "texlab",
+ "ts_ls",
+ "ty",
+ "vale_ls",
+})
diff --git a/home/nvim/options.lua b/home/nvim/options.lua
new file mode 100644
index 0000000..ee2fac2
--- /dev/null
+++ b/home/nvim/options.lua
@@ -0,0 +1,93 @@
+vim.opt.title = true -- set the title of window to the value of the titlestring
+vim.opt.titlestring = "%<%F%=%l/%L - nvim" -- what the title of the window will be set to
+vim.g.have_nerd_fonts = true
+vim.o.shell = "fish"
+vim.o.fileencoding = "utf-8"
+vim.opt.conceallevel = 0
+vim.opt.clipboard:append("unnamedplus") -- copy & paste
+vim.o.termguicolors = true -- Enable GUI colors for the terminal to get truecolor
+vim.o.mouse = "a" -- turn on mouse interaction
+vim.o.signcolumn = "yes"
+vim.o.guicursor = table.concat({
+ "n-v-c:block-Cursor/lCursor-blinkwait1000-blinkon100-blinkoff100",
+ "i-ci:ver25-Cursor/lCursor-blinkwait1000-blinkon100-blinkoff100",
+ "r:hor50-Cursor/lCursor-blinkwait100-blinkon100-blinkoff100",
+}, ",")
+
+vim.opt.completeopt = { "menu", "menuone", "noselect", "noinsert", "preview" } -- completion options
+vim.opt.shortmess:append("c")
+vim.o.updatetime = 100 -- CursorHold interval
+vim.opt.wrap = true
+vim.opt.linebreak = true
+vim.opt.breakindent = true
+vim.opt.inccommand = "split"
+vim.opt.virtualedit = "block"
+vim.opt.grepprg = "rg --vimgrep"
+vim.opt.grepformat = "%f:%l:%c:%m"
+vim.o.textwidth = 100
+vim.o.tabstop = 4
+vim.o.softtabstop = 4
+vim.o.shiftwidth = 4
+vim.o.expandtab = true
+vim.o.smartindent = true
+vim.opt.smartcase = true
+vim.o.shiftwidth = 4
+vim.o.autoindent = true
+vim.o.smarttab = true -- <tab>/<BS> indent/dedent in leading whitespace
+vim.o.list = true -- show hidden characters by default
+vim.opt.hidden = true -- required to keep multiple buffers and open multiple buffers
+
+vim.o.undofile = true
+vim.o.undodir = "/home/petri/.config/nvim/undo/"
+vim.o.backupdir = "/home/petri/.config/nvim/backup/"
+vim.o.directory = "/home/petri/.config/nvim//swp/"
+
+vim.o.number = true -- show line numbers
+vim.o.showmode = false -- we are already showing mode
+vim.o.relativenumber = true -- relative line numbers
+vim.o.hlsearch = true -- highlighted search results
+vim.o.incsearch = true -- incremental search
+vim.o.ignorecase = true -- ignore case sensetive while searching
+vim.o.colorcolumn = "100"
+vim.o.cursorline = true -- highlight current line
+vim.opt.showmatch = true -- show matching bracket
+vim.opt.matchtime = 2 -- how long to show matching bracket
+vim.opt.pumblend = 10 -- popup menu transparency
+vim.opt.winblend = 0
+vim.opt.winborder = "rounded"
+vim.opt.synmaxcol = 300
+
+vim.opt.swapfile = false
+vim.opt.autoread = true
+vim.opt.path:append("**") -- include subdirectories in search
+vim.opt.selection = "exclusive" -- Selection behavior
+vim.opt.showtabline = 2 -- show when multiple open
+
+vim.opt.foldmethod = "syntax"
+vim.opt.foldlevel = 99 -- Start with all folds open
+vim.opt.foldenable = true -- Enable folding
+
+vim.opt.scrolloff = 8
+vim.opt.sidescrolloff = 8
+
+vim.opt.formatoptions:remove({ "c", "t" })
+vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" }
+vim.opt.backspace = "indent,start,eol" -- make backspace behave like normal again
+vim.opt.splitbelow = true -- open horizontal splits below current window
+vim.opt.splitright = true -- open vertical splits to the right of the current window
+vim.opt.laststatus = 2 -- always show status line
+vim.opt.wildignore = vim.opt.wildignore + "*.o,*.rej,*.so" -- patterns to ignore during file-navigation
+vim.opt.lazyredraw = false -- faster scrolling
+
+vim.opt.spell = false
+vim.opt.spelllang = "en"
+vim.opt.wildmenu = true
+vim.opt.wildmode = "longest:full,full"
+vim.opt.wildignore:append({ "*.pyc" })
+
+vim.opt.diffopt:append("linematch:60")
+
+vim.opt.redrawtime = 10000
+vim.opt.maxmempattern = 50000
+
+vim.cmd.colorscheme("nord")
diff --git a/home/nvim/plugins/dap.lua b/home/nvim/plugins/dap.lua
new file mode 100644
index 0000000..c20ae36
--- /dev/null
+++ b/home/nvim/plugins/dap.lua
@@ -0,0 +1,147 @@
+-- Store DAP configuration in a function to load on demand
+local function setup_dap()
+ local dap = require("dap")
+ local dapview = require("dap-view")
+ local dapvt = require("nvim-dap-virtual-text")
+
+ dapview.setup({})
+
+ dapvt.setup {
+ enabled = true,
+ enabled_commands = true,
+ highlight_changed_variables = true,
+ highlight_new_as_changed = false,
+ show_stop_reason = true,
+ commented = false,
+ only_first_definition = true,
+ all_references = false,
+ clear_on_continue = false,
+ display_callback = function(variable, buf, stackframe, node, options)
+ if options.virt_text_pos == "inline" then
+ return " = " .. variable.value:gsub("%s+", " ")
+ else
+ return variable.name .. " = " .. variable.value:gsub("%s+", " ")
+ end
+ end,
+ all_frames = true,
+ virt_lines = true,
+ virt_text_win_col = nil,
+ }
+
+ -- === PYTHON ===
+ dap.adapters.python = {
+ type = "executable",
+ command = "python",
+ args = { "-m", "debugpy.adapter" },
+ }
+ dap.configurations.python = {
+ {
+ type = "python",
+ request = "launch",
+ name = "Launch file",
+ program = "${file}",
+ console = "integratedTerminal",
+ justMyCode = true,
+ },
+ {
+ type = "python",
+ request = "attach",
+ name = "Attach to process",
+ connect = { host = "127.0.0.1", port = 5678 },
+ },
+ }
+
+ -- === RUST ===
+ dap.adapters.lldb = {
+ type = "executable",
+ command = "lldb-dap",
+ name = "lldb",
+ }
+ dap.configurations.rust = {
+ {
+ name = "Launch (LLDB)",
+ type = "lldb",
+ request = "launch",
+ program = function()
+ return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/target/debug/", "file")
+ end,
+ cwd = "${workspaceFolder}",
+ stopOnEntry = false,
+ args = {},
+ runInTerminal = false,
+ },
+ {
+ name = "Attach to process (LLDB)",
+ type = "lldb",
+ request = "attach",
+ pid = require("dap.utils").pick_process,
+ args = {},
+ },
+ }
+
+ -- === GO ===
+ dap.adapters.go = {
+ type = "executable",
+ command = "dlv",
+ args = { "dap" },
+ }
+ dap.configurations.go = {
+ { type = "go", name = "Debug main.go", request = "launch", program = "${file}" },
+ { type = "go", name = "Debug package", request = "launch", program = "${workspaceFolder}" },
+ { type = "go", name = "Attach to process", request = "attach", processId = require("dap.utils").pick_process },
+ }
+
+ -- === EVENT LISTENERS ===
+ dap.listeners.after.event_initialized["dapview_config"] = function() dapview.open() end
+ dap.listeners.before.event_terminated["dapview_config"] = function() dapview.close() end
+ dap.listeners.before.event_exited["dapview_config"] = function() dapview.close() end
+end
+
+-- Define custom commands to trigger DAP setup
+local function define_dap_commands()
+ vim.api.nvim_create_user_command("DapContinue", function()
+ setup_dap()
+ require("dap").continue()
+ end, {})
+ vim.api.nvim_create_user_command("DapToggleBreakpoint", function()
+ setup_dap()
+ require("dap").toggle_breakpoint()
+ end, {})
+ vim.api.nvim_create_user_command("DapStepOver", function()
+ setup_dap()
+ require("dap").step_over()
+ end, {})
+ vim.api.nvim_create_user_command("DapStepInto", function()
+ setup_dap()
+ require("dap").step_into()
+ end, {})
+ vim.api.nvim_create_user_command("DapStepOut", function()
+ setup_dap()
+ require("dap").step_out()
+ end, {})
+ vim.api.nvim_create_user_command("DapTerminate", function()
+ setup_dap()
+ require("dap").terminate()
+ end, {})
+end
+
+-- Create autocommand group for DAP
+local augroup = vim.api.nvim_create_augroup("DapLazyLoad", { clear = true })
+
+-- Load DAP on filetypes
+vim.api.nvim_create_autocmd("FileType", {
+ group = augroup,
+ pattern = { "python", "rust", "go" },
+ callback = function()
+ define_dap_commands()
+ end,
+})
+
+-- Load DAP on specific commands
+vim.api.nvim_create_autocmd("CmdUndefined", {
+ group = augroup,
+ pattern = { "DapContinue", "DapToggleBreakpoint", "DapStepOver", "DapStepInto", "DapStepOut", "DapTerminate" },
+ callback = function()
+ define_dap_commands()
+ end,
+})
diff --git a/home/nvim/plugins/mini.lua b/home/nvim/plugins/mini.lua
new file mode 100644
index 0000000..f41f1b4
--- /dev/null
+++ b/home/nvim/plugins/mini.lua
@@ -0,0 +1,14 @@
+require("mini.animate").setup()
+require("mini.diff").setup()
+require("mini.files").setup()
+require("mini.git").setup()
+require("mini.icons").setup()
+require("mini.notify").setup()
+require("mini.pick").setup()
+require("mini.sessions").setup()
+require("mini.starter").setup()
+require("mini.statusline").setup()
+require("mini.tabline").setup({
+ show_icons = true
+})
+require("mini.completion").setup()
diff --git a/home/nvim/plugins/other.lua b/home/nvim/plugins/other.lua
new file mode 100644
index 0000000..10efd0e
--- /dev/null
+++ b/home/nvim/plugins/other.lua
@@ -0,0 +1,5 @@
+-- hardtime
+require("hardtime").setup()
+
+vim.g.nord_italic = true
+require("nord").set()
diff --git a/home/nvim/plugins/treesitter.lua b/home/nvim/plugins/treesitter.lua
new file mode 100644
index 0000000..968af11
--- /dev/null
+++ b/home/nvim/plugins/treesitter.lua
@@ -0,0 +1,81 @@
+local function disable(lang, buf)
+ local max_filesize = 100 * 1024 -- 100 KB
+ local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
+ if ok and stats and stats.size > max_filesize then
+ return true
+ end
+end
+
+require("nvim-treesitter.configs").setup({
+ ensure_installed = {},
+ sync_install = false,
+ ignore_install = {},
+ modules = {},
+ auto_install = false,
+ highlight = {
+ enable = true,
+ disable = disable,
+ additional_vim_regex_highlighting = false,
+ },
+ indent = { enable = true, disable = { "rust", "lua", "python", "golang", "nix", "json", "html", "javascript" } },
+ incremental_selection = {
+ enable = true,
+ keymaps = {
+ init_selection = "<leader>vv",
+ node_incremental = "+",
+ scope_incremental = false,
+ node_decremental = "_",
+ },
+ },
+ textobjects = {
+ select = {
+ enable = true,
+ lookahead = true,
+ keymaps = {
+ -- You can use the capture groups defined in textobjects.scm
+ ["af"] = { query = "@function.outer", desc = "around a function" },
+ ["if"] = { query = "@function.inner", desc = "inner part of a function" },
+ ["ac"] = { query = "@class.outer", desc = "around a class" },
+ ["ic"] = { query = "@class.inner", desc = "inner part of a class" },
+ ["ai"] = { query = "@conditional.outer", desc = "around an if statement" },
+ ["ii"] = { query = "@conditional.inner", desc = "inner part of an if statement" },
+ ["al"] = { query = "@loop.outer", desc = "around a loop" },
+ ["il"] = { query = "@loop.inner", desc = "inner part of a loop" },
+ ["ap"] = { query = "@parameter.outer", desc = "around parameter" },
+ ["ip"] = { query = "@parameter.inner", desc = "inside a parameter" },
+ },
+ selection_modes = {
+ ["@parameter.outer"] = "v", -- charwise
+ ["@parameter.inner"] = "v", -- charwise
+ ["@function.outer"] = "v", -- charwise
+ ["@conditional.outer"] = "V", -- linewise
+ ["@loop.outer"] = "V", -- linewise
+ ["@class.outer"] = "<c-v>", -- blockwise
+ },
+ include_surrounding_whitespace = false,
+ },
+ move = {
+ enable = true,
+ set_jumps = true, -- whether to set jumps in the jumplist
+ goto_previous_start = {
+ ["[f"] = { query = "@function.outer", desc = "Previous function" },
+ ["[c"] = { query = "@class.outer", desc = "Previous class" },
+ ["[p"] = { query = "@parameter.inner", desc = "Previous parameter" },
+ },
+ goto_next_start = {
+ ["]f"] = { query = "@function.outer", desc = "Next function" },
+ ["]c"] = { query = "@class.outer", desc = "Next class" },
+ ["]p"] = { query = "@parameter.inner", desc = "Next parameter" },
+ },
+ },
+ swap = {
+ enable = true,
+ swap_next = {
+ ["<leader>a"] = "@parameter.inner",
+ },
+ swap_previous = {
+ ["<leader>A"] = "@parameter.inner",
+ },
+ },
+ },
+})
diff --git a/home/nvim/plugins/undotree.lua b/home/nvim/plugins/undotree.lua
new file mode 100644
index 0000000..b6b9276
--- /dev/null
+++ b/home/nvim/plugins/undotree.lua
@@ -0,0 +1 @@
+vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle)
diff --git a/home/nvim/plugins/which.lua b/home/nvim/plugins/which.lua
new file mode 100644
index 0000000..09aa796
--- /dev/null
+++ b/home/nvim/plugins/which.lua
@@ -0,0 +1,6 @@
+local wk = require("which-key")
+wk.add({
+ "<leader>?",
+ function() require("which-key").show({ global = false }) end,
+ desc = "Buffer Local Keymaps (which-key)",
+})
diff --git a/home/quickshell/PopupContext.qml b/home/quickshell/PopupContext.qml
new file mode 100644
index 0000000..6f007c8
--- /dev/null
+++ b/home/quickshell/PopupContext.qml
@@ -0,0 +1,6 @@
+import QtQuick
+
+// Tracks which popup of a set is active.
+QtObject {
+ property var popup: null;
+}
diff --git a/home/quickshell/README.md b/home/quickshell/README.md
new file mode 100644
index 0000000..0458afa
--- /dev/null
+++ b/home/quickshell/README.md
@@ -0,0 +1,13 @@
+## Quickshell Titus Config
+
+This is a minimal DWM style bar for Hyprland. HOWEVER, most of the examples online do a lot of crazy things. If you can think it up you can do it in QUICKSHELL!
+
+Try it today!
+
+### Dependencies
+
+Arch Linux
+
+```
+yay -S qt6-5compat quickshell
+```
diff --git a/home/quickshell/Theme.qml b/home/quickshell/Theme.qml
new file mode 100644
index 0000000..e8777db
--- /dev/null
+++ b/home/quickshell/Theme.qml
@@ -0,0 +1,87 @@
+pragma Singleton
+
+import QtQuick
+import Quickshell
+
+Singleton {
+ property Item get: nordic
+
+ Item {
+ id: windowsXP
+
+ property string barBgColor: "#88235EDC"
+ property string buttonBorderColor: "#99000000"
+ property bool buttonBorderShadow: false
+ property string buttonBackgroundColor: "#1111CC"
+ property bool onTop: false
+ property string iconColor: "green"
+ property string iconPressedColor: "green"
+ property Gradient barGradient: black.barGradient
+ }
+
+ Item {
+ id: black
+
+ property string barBgColor: "#cc000000"
+ property string buttonBorderColor: "#BBBBBB"
+ property string buttonBackgroundColor: "#222222"
+ property bool buttonBorderShadow: true
+ property bool onTop: true
+ property string iconColor: "blue"
+ property string iconPressedColor: "dark_blue"
+ }
+
+ Item {
+ id: nordic
+
+ // Nord color palette
+ property string barBgColor: "#aa2E3440" // Nord0 - Polar Night
+ property string buttonBorderColor: "#4C566A" // Nord3 - Polar Night
+ property string buttonBackgroundColor: "#3D4550"
+ property bool buttonBorderShadow: true
+ property bool onTop: true
+ property string iconColor: "#88C0D0" // Nord7 - Frost
+ property string iconPressedColor: "#81A1C1" // Nord9 - Frost
+ }
+
+ Item {
+ id: cyberpunk
+
+ // Tokyo Neon color palette
+ property string barBgColor: "#881A0B2E" // Deep purple-black
+ property string buttonBorderColor: "#FF2A6D" // Neon pink
+ property string buttonBackgroundColor: "#1A1A2E" // Dark blue-black
+ property bool buttonBorderShadow: true
+ property bool onTop: true
+ property string iconColor: "#05D9E8" // Electric blue
+ property string iconPressedColor: "#FF2A6D" // Neon pink
+ }
+
+ Item {
+ id: material
+
+ // Material Design 3 color palette
+ property string barBgColor: "#cc1F1F1F" // Surface dark
+ property string buttonBorderColor: "#2D2D2D" // Surface variant
+ property string buttonBackgroundColor: "#2D2D2D" // Surface variant
+ property bool buttonBorderShadow: true
+ property bool onTop: true
+ property string iconColor: "#90CAF9" // Primary light
+ property string iconPressedColor: "#64B5F6" // Primary medium
+ }
+
+ Item {
+ id: catppuccin
+
+ // Catppuccin Mocha color palette
+ property string barBgColor: "#aa1E1E2E" // Base
+ property string buttonBorderColor: "#313244" // Surface0
+ property string buttonBackgroundColor: "#313244" // Surface0
+ property bool buttonBorderShadow: true
+ property bool onTop: true
+ property string iconColor: "#89B4FA" // Blue
+ property string iconPressedColor: "#74C7EC" // Sapphire
+ }
+
+}
+
diff --git a/home/quickshell/bar/Bar.qml b/home/quickshell/bar/Bar.qml
new file mode 100644
index 0000000..5f5cae8
--- /dev/null
+++ b/home/quickshell/bar/Bar.qml
@@ -0,0 +1,115 @@
+import Quickshell
+import Quickshell.Io
+import Quickshell.Hyprland
+import QtQuick
+import QtQuick.Layouts
+import "blocks" as Blocks
+import "root:/"
+
+Scope {
+ IpcHandler {
+ target: "bar"
+
+ function toggleVis(): void {
+ // Toggle visibility of all bar instances
+ for (let i = 0; i < Quickshell.screens.length; i++) {
+ barInstances[i].visible = !barInstances[i].visible;
+ }
+ }
+ }
+
+ property var barInstances: []
+
+ Variants {
+ model: Quickshell.screens
+
+ PanelWindow {
+ id: bar
+ property var modelData
+ screen: modelData
+
+ Component.onCompleted: {
+ barInstances.push(bar);
+ }
+
+ color: "transparent"
+
+ Rectangle {
+ id: highlight
+ anchors.fill: parent
+ color: Theme.get.barBgColor
+ }
+
+ height: 30
+
+ visible: true
+
+ anchors {
+ top: Theme.get.onTop
+ bottom: !Theme.get.onTop
+ left: true
+ right: true
+ }
+
+ RowLayout {
+ id: allBlocks
+ spacing: 0
+ anchors.fill: parent
+
+ // Left side
+ RowLayout {
+ id: leftBlocks
+ spacing: 10
+ Layout.alignment: Qt.AlignLeft
+ Layout.fillWidth: true
+
+ //Blocks.Icon {}
+ Blocks.Workspaces {}
+ }
+
+ Blocks.ActiveWorkspace {
+ id: activeWorkspace
+ Layout.leftMargin: 10
+ anchors.centerIn: undefined
+
+ chopLength: {
+ var space = Math.floor(bar.width - (rightBlocks.implicitWidth + leftBlocks.implicitWidth))
+ return space * 0.08;
+ }
+
+ text: {
+ var str = activeWindowTitle
+ return str.length > chopLength ? str.slice(0, chopLength) + '...' : str;
+ }
+
+ color: {
+ return Hyprland.focusedMonitor == Hyprland.monitorFor(screen)
+ ? "#FFFFFF" : "#CCCCCC"
+ }
+ }
+
+ // Without this filler item, the active window block will be centered
+ // despite setting left alignment
+ Item {
+ Layout.fillWidth: true
+ }
+
+ // Right side
+ RowLayout {
+ id: rightBlocks
+ spacing: 0
+ Layout.alignment: Qt.AlignRight
+ Layout.fillWidth: true
+
+ Blocks.SystemTray {}
+ Blocks.Memory {}
+ Blocks.Sound {}
+ Blocks.Battery {}
+ Blocks.Date {}
+ Blocks.Time {}
+ }
+ }
+ }
+ }
+}
+
diff --git a/home/quickshell/bar/BarBlock.qml b/home/quickshell/bar/BarBlock.qml
new file mode 100644
index 0000000..edd4aca
--- /dev/null
+++ b/home/quickshell/bar/BarBlock.qml
@@ -0,0 +1,75 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+
+Rectangle {
+ id: root
+ Layout.preferredWidth: contentContainer.implicitWidth + 10
+ Layout.preferredHeight: 30
+
+ property Item content
+ property Item mouseArea: mouseArea
+
+ property string text
+ property bool dim: false
+ property bool underline
+ property var onClicked: function() {}
+ property int leftPadding
+ property int rightPadding
+
+ property string hoveredBgColor: "#666666"
+
+ // Background color
+ color: {
+ if (mouseArea.containsMouse)
+ return hoveredBgColor;
+ return "transparent";
+ }
+
+ states: [
+ State {
+ when: mouseArea.containsMouse
+ PropertyChanges {
+ target: root
+ }
+ }
+ ]
+
+ Behavior on color {
+ ColorAnimation {
+ duration: 200
+ }
+ }
+
+ Item {
+ // Contents of the bar block
+ id: contentContainer
+ implicitWidth: content.implicitWidth
+ implicitHeight: content.implicitHeight
+ anchors.centerIn: parent
+ children: content
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: root
+ hoverEnabled: true
+ acceptedButtons: Qt.LeftButton
+ onClicked: root.onClicked()
+ }
+
+ // While line underneath workspace
+ Rectangle {
+ id: wsLine
+ width: parent.width
+ height: 2
+
+ color: {
+ if (parent.underline)
+ return "white";
+ return "transparent";
+ }
+ anchors.bottom: parent.bottom
+ }
+}
+
diff --git a/home/quickshell/bar/BarText.qml b/home/quickshell/bar/BarText.qml
new file mode 100644
index 0000000..4cf42cc
--- /dev/null
+++ b/home/quickshell/bar/BarText.qml
@@ -0,0 +1,57 @@
+import Quickshell
+import Quickshell.Io
+import Quickshell.Widgets
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Effects
+import Qt5Compat.GraphicalEffects
+
+Text {
+ property string mainFont: "FiraCode"
+ property string symbolFont: "Symbols Nerd Font Mono"
+ property int pointSize: 12
+ property int symbolSize: pointSize * 1.4
+ property string symbolText
+ property bool dim
+ text: wrapSymbols(symbolText)
+ anchors.centerIn: parent
+ color: dim ? "#CCCCCC" : "white"
+ textFormat: Text.RichText
+ font {
+ family: mainFont
+ pointSize: pointSize
+ }
+
+ Text {
+ visible: false
+ id: textcopy
+ text: parent.text
+ textFormat: parent.textFormat
+ color: parent.color
+ font: parent.font
+ }
+
+ DropShadow {
+ anchors.fill: parent
+ horizontalOffset: 1
+ verticalOffset: 1
+ color: "#000000"
+ source: textcopy
+ }
+
+ function wrapSymbols(text) {
+ if (!text)
+ return ""
+
+ const isSymbol = (codePoint) =>
+ (codePoint >= 0xE000 && codePoint <= 0xF8FF) // Private Use Area
+ || (codePoint >= 0xF0000 && codePoint <= 0xFFFFF) // Supplementary Private Use Area-A
+ || (codePoint >= 0x100000 && codePoint <= 0x10FFFF); // Supplementary Private Use Area-B
+
+ return text.replace(/./gu, (c) => isSymbol(c.codePointAt(0))
+ ? `<span style='font-family: ${symbolFont}; letter-spacing: 5px; font-size: ${symbolSize}px'>${c}</span>`
+ // ? c
+ : c);
+ }
+}
+
diff --git a/home/quickshell/bar/Notification.qml b/home/quickshell/bar/Notification.qml
new file mode 100644
index 0000000..b86a966
--- /dev/null
+++ b/home/quickshell/bar/Notification.qml
@@ -0,0 +1,11 @@
+import QtQuick
+
+Text {
+ required property int id
+ required property string body
+ required property string summary
+ property int margin
+
+ text: `- ${summary}: ${body}`
+}
+
diff --git a/home/quickshell/bar/NotificationPanel.qml b/home/quickshell/bar/NotificationPanel.qml
new file mode 100644
index 0000000..0ef8712
--- /dev/null
+++ b/home/quickshell/bar/NotificationPanel.qml
@@ -0,0 +1,101 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Wayland
+import Quickshell.Services.Notifications
+
+PanelWindow {
+ // required property font custom_font
+ required property color text_color
+ property list<QtObject> notification_objects
+
+ width: 500
+ height: 600
+
+ color: "#171a18"
+
+ WlrLayershell.layer: WlrLayer.Overlay
+
+ Rectangle {
+ border.width: 5
+ border.color: "#8ec07c"
+ anchors.fill: parent
+ color: "transparent"
+
+ ColumnLayout {
+ id: content
+ anchors {
+ left: parent.left
+ leftMargin: 10
+ right: parent.right
+ rightMargin: 10
+ top: parent.top
+ topMargin: 10
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+
+ Text {
+ Layout.fillWidth: true
+ text: "Notifications:"
+ // font: custom_font
+ color: text_color
+ }
+
+ Text {
+ text: "clear"
+ // font: custom_font
+ color: text_color
+
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ onTapped: {
+ server.trackedNotifications.values.forEach((notification) => {
+ notification.tracked = false
+ })
+ notification_objects.forEach((object) => {
+ object.destroy();
+ })
+ notification_objects = [];
+ }
+ }
+
+ HoverHandler {
+ id: mouse
+ acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
+ cursorShape: Qt.PointingHandCursor
+ }
+ }
+ }
+ }
+ }
+
+ NotificationServer {
+ id: server
+ onNotification: (notification) => {
+ notification.tracked = true
+ console.log(JSON.stringify(notification));
+ var notification_component = Qt.createComponent("Notification.qml");
+ var notification_object = notification_component
+ .createObject(content,
+ {
+ id: notification.id,
+ body: notification.body,
+ summary: notification.summary,
+ // font: custom_font,
+ color: text_color,
+ margin: 10
+ }
+ )
+ if (notification_object == null) {
+ console.log("Error creating notification")
+ } else {
+ notification_objects.push(notification_object);
+ }
+ }
+ }
+}
+
diff --git a/home/quickshell/bar/Tooltip.qml b/home/quickshell/bar/Tooltip.qml
new file mode 100644
index 0000000..7ab247d
--- /dev/null
+++ b/home/quickshell/bar/Tooltip.qml
@@ -0,0 +1,89 @@
+import QtQuick
+import Quickshell
+import "root:/" // for Globals
+
+LazyLoader {
+ id: root
+
+ // The item to display the tooltip at. If set to null the tooltip will be hidden.
+ property Item relativeItem: null
+
+ // Tracks the item after relativeItem is unset.
+ property Item displayItem: null
+
+ property PopupContext popupContext: Globals.popupContext
+
+ property bool hoverable: false;
+ readonly property bool hovered: item?.hovered ?? false
+
+ // The content to show in the tooltip.
+ required default property Component contentDelegate
+
+ active: displayItem != null && popupContext.popup == this
+
+ onRelativeItemChanged: {
+ if (relativeItem == null) {
+ if (item != null) item.hideTimer.start();
+ } else {
+ if (item != null) item.hideTimer.stop();
+ displayItem = relativeItem;
+ popupContext.popup = this;
+ }
+ }
+
+ PopupWindow {
+ anchor {
+ window: root.displayItem.QsWindow.window
+ rect.y: anchor.window.height + 3
+ rect.x: anchor.window.contentItem.mapFromItem(root.displayItem, root.displayItem.width / 2, 0).x
+ edges: Edges.Top
+ gravity: Edges.Bottom
+ }
+
+ visible: true
+
+ property alias hovered: body.containsMouse;
+
+ property Timer hideTimer: Timer {
+ interval: 250
+
+ // unloads the popup by causing active to become false
+ onTriggered: root.popupContext.popup = null;
+ }
+
+ color: "transparent"
+
+ // don't accept mouse input if !hoverable
+ Region { id: emptyRegion }
+ mask: root.hoverable ? null : emptyRegion
+
+ width: body.implicitWidth
+ height: body.implicitHeight
+
+ MouseArea {
+ id: body
+
+ anchors.fill: parent
+ implicitWidth: content.implicitWidth + 10
+ implicitHeight: content.implicitHeight + 10
+
+ hoverEnabled: root.hoverable
+
+ Rectangle {
+ anchors.fill: parent
+
+ radius: 5
+ border.width: 1
+ color: palette.active.toolTipBase
+ border.color: palette.active.light
+
+ Loader {
+ id: content
+ anchors.centerIn: parent
+ sourceComponent: contentDelegate
+ active: true
+ }
+ }
+ }
+ }
+}
diff --git a/home/quickshell/bar/blocks/ActiveWorkspace.qml b/home/quickshell/bar/blocks/ActiveWorkspace.qml
new file mode 100644
index 0000000..7969212
--- /dev/null
+++ b/home/quickshell/bar/blocks/ActiveWorkspace.qml
@@ -0,0 +1,34 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell.Io
+import Quickshell.Hyprland
+import "../"
+
+BarText {
+ // text: {
+ // var str = activeWindowTitle
+ // return str.length > chopLength ? str.slice(0, chopLength) + '...' : str;
+ // }
+
+ property int chopLength
+ property string activeWindowTitle
+
+ Process {
+ id: titleProc
+ command: ["sh", "-c", "hyprctl activewindow | grep title: | sed 's/^[^:]*: //'"]
+ running: true
+
+ stdout: SplitParser {
+ onRead: data => activeWindowTitle = data
+ }
+ }
+
+ Component.onCompleted: {
+ Hyprland.rawEvent.connect(hyprEvent)
+ }
+
+ function hyprEvent(e) {
+ titleProc.running = true
+ }
+}
+
diff --git a/home/quickshell/bar/blocks/Battery.qml b/home/quickshell/bar/blocks/Battery.qml
new file mode 100644
index 0000000..dd52f7f
--- /dev/null
+++ b/home/quickshell/bar/blocks/Battery.qml
@@ -0,0 +1,50 @@
+import QtQuick
+import Quickshell.Io
+import "../"
+
+BarBlock {
+ property string battery
+ property bool hasBattery: false
+ visible: hasBattery
+
+ content: BarText {
+ symbolText: battery
+ }
+
+ Process {
+ id: batteryCheck
+ command: ["sh", "-c", "test -d /sys/class/power_supply/BAT*"]
+ running: true
+ onExited: function(exitCode) { hasBattery = exitCode === 0 }
+ }
+
+ Process {
+ id: batteryProc
+ // Modify command to get both capacity and status in one call
+ command: ["sh", "-c", "echo $(cat /sys/class/power_supply/BAT*/capacity),$(cat /sys/class/power_supply/BAT*/status)"]
+ running: hasBattery
+
+ stdout: SplitParser {
+ onRead: function(data) {
+ const [capacityStr, status] = data.trim().split(',')
+ const capacity = parseInt(capacityStr)
+ let batteryIcon = "󰂂"
+ if (capacity <= 20) batteryIcon = "󰁺"
+ else if (capacity <= 40) batteryIcon = "󰁽"
+ else if (capacity <= 60) batteryIcon = "󰁿"
+ else if (capacity <= 80) batteryIcon = "󰂁"
+ else batteryIcon = "󰂂"
+
+ const symbol = status === "Charging" ? "🔌" : batteryIcon
+ battery = `${symbol} ${capacity}%`
+ }
+ }
+ }
+
+ Timer {
+ interval: 1000
+ running: hasBattery
+ repeat: true
+ onTriggered: batteryProc.running = true
+ }
+}
diff --git a/home/quickshell/bar/blocks/Date.qml b/home/quickshell/bar/blocks/Date.qml
new file mode 100644
index 0000000..11ce193
--- /dev/null
+++ b/home/quickshell/bar/blocks/Date.qml
@@ -0,0 +1,10 @@
+import QtQuick
+import "../"
+
+BarBlock {
+ id: text
+ content: BarText {
+ symbolText: ` ${Datetime.date}`
+ }
+}
+
diff --git a/home/quickshell/bar/blocks/Datetime.qml b/home/quickshell/bar/blocks/Datetime.qml
new file mode 100644
index 0000000..743e785
--- /dev/null
+++ b/home/quickshell/bar/blocks/Datetime.qml
@@ -0,0 +1,31 @@
+pragma Singleton
+
+import Quickshell
+import Quickshell.Io
+import QtQuick
+
+Singleton {
+ property string time;
+ property string date;
+
+ Process {
+ id: dateProc
+ command: ["date", "+%a %e %b|%R"]
+ running: true
+
+ stdout: SplitParser {
+ onRead: data => {
+ date = data.split("|")[0]
+ time = data.split("|")[1]
+ }
+ }
+ }
+
+ Timer {
+ interval: 1000
+ running: true
+ repeat: true
+ onTriggered: dateProc.running = true
+ }
+}
+
diff --git a/home/quickshell/bar/blocks/Icon.qml b/home/quickshell/bar/blocks/Icon.qml
new file mode 100644
index 0000000..c111a7e
--- /dev/null
+++ b/home/quickshell/bar/blocks/Icon.qml
@@ -0,0 +1,146 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Io
+import Quickshell.Widgets
+import Qt5Compat.GraphicalEffects
+import "../"
+import "root:/"
+
+BarBlock {
+ id: root
+ Layout.preferredWidth: 20
+
+ content: BarText {
+ text: "󰣇"
+ pointSize: 24
+ anchors.horizontalCenterOffset: 4
+ anchors.verticalCenterOffset: 3
+ }
+
+ color: "transparent"
+
+ Process {
+ id: appListProc
+ command: ["sh", "-c", "for f in /usr/share/applications/*.desktop; do if ! grep -qi 'terminal=true' \"$f\"; then name=$(grep -i '^Name=' \"$f\" | head -n1 | cut -d= -f2); basename=$(basename \"$f\" .desktop); echo \"$name|$basename|$f\"; fi; done"]
+ running: false
+ stdout: SplitParser {
+ onRead: data => {
+ const [appName, launchName, desktopFile] = data.trim().split("|")
+ if (appName && launchName && desktopFile) {
+ appListModel.append({ name: appName, launchName: launchName, path: desktopFile })
+ }
+ }
+ }
+ }
+
+ Process {
+ id: appLauncher
+ running: false
+ command: ["gtk-launch"]
+ }
+
+ ListModel {
+ id: appListModel
+ }
+
+ PopupWindow {
+ id: menuWindow
+ width: 300
+ height: 400
+ visible: false
+
+ anchor {
+ window: root.QsWindow?.window
+ edges: Edges.Bottom
+ gravity: Edges.Top
+ }
+
+ FocusScope {
+ anchors.fill: parent
+ focus: true
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onExited: {
+ if (!containsMouse) {
+ closeTimer.start()
+ }
+ }
+ onEntered: closeTimer.stop()
+
+ Timer {
+ id: closeTimer
+ interval: 500
+ onTriggered: menuWindow.visible = false
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#2E3440" // Using Nord theme color
+ border.color: "#4C566A"
+ border.width: 1
+ radius: 4
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 10
+ spacing: 5
+
+ ListView {
+ id: appListView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ clip: true
+ model: appListModel
+ delegate: Rectangle {
+ width: parent.width
+ height: 35
+ color: mouseArea.containsMouse ? "#4C566A" : "transparent"
+ radius: 4
+
+ Text {
+ anchors.fill: parent
+ anchors.leftMargin: 10
+ text: model.name
+ color: "white"
+ font.pixelSize: 12
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ console.log("Launching:", model.launchName, "from", model.path)
+ appLauncher.command = ["gtk-launch", model.launchName]
+ appLauncher.running = true
+ menuWindow.visible = false
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function filterApps() {
+ const searchText = searchField.text.toLowerCase()
+ for (let i = 0; i < appListModel.count; i++) {
+ const item = appListModel.get(i)
+ item.visible = item.name.toLowerCase().includes(searchText)
+ }
+ }
+ onClicked: function() {
+ if (!menuWindow.visible) {
+ appListModel.clear()
+ appListProc.running = true
+ }
+ menuWindow.visible = !menuWindow.visible
+ }
+} \ No newline at end of file
diff --git a/home/quickshell/bar/blocks/Memory.qml b/home/quickshell/bar/blocks/Memory.qml
new file mode 100644
index 0000000..4a931c1
--- /dev/null
+++ b/home/quickshell/bar/blocks/Memory.qml
@@ -0,0 +1,31 @@
+import QtQuick
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Io
+import "../"
+
+BarBlock {
+ id: text
+ content: BarText {
+ symbolText: `- ${Math.floor(percentFree)}%`
+ }
+
+ property real percentFree
+
+ Process {
+ id: memProc
+ command: ["sh", "-c", "free | grep Mem | awk '{print $3/$2 * 100.0}'"]
+ running: true
+
+ stdout: SplitParser {
+ onRead: data => percentFree = data
+ }
+ }
+
+ Timer {
+ interval: 2000
+ running: true
+ repeat: true
+ onTriggered: memProc.running = true
+ }
+}
diff --git a/home/quickshell/bar/blocks/Notifications.qml b/home/quickshell/bar/blocks/Notifications.qml
new file mode 100644
index 0000000..3871cc4
--- /dev/null
+++ b/home/quickshell/bar/blocks/Notifications.qml
@@ -0,0 +1,34 @@
+import QtQuick
+import Quickshell.Services.Notifications
+import "../"
+
+BarBlock {
+ id: root
+ property bool showNotification: false
+
+ text: " " + notifServer.trackedNotifications.values.length
+ onClicked: function() {
+ showNotification = !showNotification
+ }
+
+ NotificationServer {
+ id: notifServer
+ onNotification: (notification) => {
+ notification.tracked = true
+ }
+ }
+
+ NotificationPanel {
+ text_color: root.color
+ visible: showNotification
+
+ anchors {
+ top: parent.top
+ }
+
+ margins {
+ top: 10
+ }
+ }
+}
+
diff --git a/home/quickshell/bar/blocks/Sound.qml b/home/quickshell/bar/blocks/Sound.qml
new file mode 100644
index 0000000..e8be70b
--- /dev/null
+++ b/home/quickshell/bar/blocks/Sound.qml
@@ -0,0 +1,176 @@
+import QtQuick
+import QtQuick.Controls
+import Quickshell
+import Quickshell.Services.Pipewire
+import Quickshell.Io
+import "../"
+import "root:/"
+
+BarBlock {
+ id: root
+ property var sink: Pipewire.defaultAudioSink
+
+ PwObjectTracker {
+ objects: [Pipewire.defaultAudioSink]
+ onObjectsChanged: {
+ sink = Pipewire.defaultAudioSink
+ if (sink?.audio) {
+ sink.audio.volumeChanged.connect(updateVolume)
+ }
+ }
+ }
+
+ function updateVolume() {
+ if (sink?.audio) {
+ const icon = sink.audio.muted ? "󰖁" : "󰕾"
+ content.symbolText = `${icon} ${Math.round(sink.audio.volume * 100)}%`
+ }
+ }
+
+ content: BarText { symbolText: `${sink?.audio?.muted ? "󰖁" : "󰕾"} ${Math.round(sink?.audio?.volume * 100)}%` }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: toggleMenu()
+ onWheel: function(event) {
+ if (sink?.audio) {
+ sink.audio.volume = Math.max(0, Math.min(1, sink.audio.volume + (event.angleDelta.y / 120) * 0.05))
+ }
+ }
+ }
+
+ Process {
+ id: pavucontrol
+ command: ["pavucontrol"]
+ running: false
+ }
+
+ PopupWindow {
+ id: menuWindow
+ width: 200
+ height: 150
+ visible: false
+
+ anchor {
+ window: root.QsWindow?.window
+ edges: Edges.Bottom
+ gravity: Edges.Top
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onExited: {
+ if (!containsMouse) {
+ closeTimer.start()
+ }
+ }
+ onEntered: closeTimer.stop()
+
+ Timer {
+ id: closeTimer
+ interval: 500
+ onTriggered: menuWindow.visible = false
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#2c2c2c"
+ border.color: "#3c3c3c"
+ border.width: 1
+ radius: 4
+
+ Column {
+ anchors.fill: parent
+ anchors.margins: 10
+ spacing: 10
+
+ // Volume Slider
+ Rectangle {
+ width: parent.width
+ height: 35
+ color: "transparent"
+
+ Slider {
+ id: volumeSlider
+ anchors.fill: parent
+ from: 0
+ to: 1
+ value: sink?.audio?.volume || 0
+ onValueChanged: {
+ if (sink?.audio) {
+ sink.audio.volume = value
+ }
+ }
+
+ background: Rectangle {
+ x: volumeSlider.leftPadding
+ y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2
+ width: volumeSlider.availableWidth
+ height: 4
+ radius: 2
+ color: "#3c3c3c"
+
+ Rectangle {
+ width: volumeSlider.visualPosition * parent.width
+ height: parent.height
+ color: "#4a9eff"
+ radius: 2
+ }
+ }
+
+ handle: Rectangle {
+ x: volumeSlider.leftPadding + volumeSlider.visualPosition * (volumeSlider.availableWidth - width)
+ y: volumeSlider.topPadding + volumeSlider.availableHeight / 2 - height / 2
+ width: 16
+ height: 16
+ radius: 8
+ color: volumeSlider.pressed ? "#4a9eff" : "#ffffff"
+ border.color: "#3c3c3c"
+ }
+ }
+ }
+
+ Repeater {
+ model: [
+ { text: sink?.audio?.muted ? "Unmute" : "Mute", action: () => sink?.audio && (sink.audio.muted = !sink.audio.muted) },
+ { text: "Pavucontrol", action: () => { pavucontrol.running = true; menuWindow.visible = false } }
+ ]
+
+ Rectangle {
+ width: parent.width
+ height: 35
+ color: mouseArea.containsMouse ? "#3c3c3c" : "transparent"
+ radius: 4
+
+ Text {
+ anchors.fill: parent
+ anchors.leftMargin: 10
+ text: modelData.text
+ color: "white"
+ font.pixelSize: 12
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: {
+ modelData.action()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function toggleMenu() {
+ if (root.QsWindow?.window?.contentItem) {
+ menuWindow.anchor.rect = root.QsWindow.window.contentItem.mapFromItem(root, 0, -menuWindow.height - 5, root.width, root.height)
+ menuWindow.visible = !menuWindow.visible
+ }
+ }
+} \ No newline at end of file
diff --git a/home/quickshell/bar/blocks/SystemTray.qml b/home/quickshell/bar/blocks/SystemTray.qml
new file mode 100644
index 0000000..15c4691
--- /dev/null
+++ b/home/quickshell/bar/blocks/SystemTray.qml
@@ -0,0 +1,80 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Widgets
+import Quickshell.Services.SystemTray
+import "root:/bar"
+
+RowLayout {
+ spacing: 5
+
+ Repeater {
+ model: ScriptModel {
+ values: {[...SystemTray.items.values]
+ .filter((item) => {
+ return (item.id != "spotify-client"
+ && item.id != "chrome_status_icon_1")
+ })
+ }
+ }
+
+ MouseArea {
+ id: delegate
+ required property SystemTrayItem modelData
+ property alias item: delegate.modelData
+
+ Layout.fillHeight: true
+ implicitWidth: icon.implicitWidth + 5
+
+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
+ hoverEnabled: true
+
+ onClicked: event => {
+ if (event.button == Qt.LeftButton) {
+ item.activate();
+ } else if (event.button == Qt.MiddleButton) {
+ item.secondaryActivate();
+ } else if (event.button == Qt.RightButton) {
+ menuAnchor.open();
+ }
+ }
+
+ onWheel: event => {
+ event.accepted = true;
+ const points = event.angleDelta.y / 120
+ item.scroll(points, false);
+ }
+
+ IconImage {
+ id: icon
+ anchors.centerIn: parent
+ source: item.icon
+ implicitSize: 16
+ }
+
+ QsMenuAnchor {
+ id: menuAnchor
+ menu: item.menu
+
+ anchor.window: delegate.QsWindow.window
+ anchor.adjustment: PopupAdjustment.Flip
+
+ anchor.onAnchoring: {
+ const window = delegate.QsWindow.window;
+ const widgetRect = window.contentItem.mapFromItem(delegate, 0, delegate.height, delegate.width, delegate.height);
+
+ menuAnchor.anchor.rect = widgetRect;
+ }
+ }
+
+ Tooltip {
+ relativeItem: delegate.containsMouse ? delegate : null
+
+ Label {
+ text: delegate.item.tooltipTitle || delegate.item.id
+ }
+ }
+ }
+ }
+}
diff --git a/home/quickshell/bar/blocks/Time.qml b/home/quickshell/bar/blocks/Time.qml
new file mode 100644
index 0000000..5650fcb
--- /dev/null
+++ b/home/quickshell/bar/blocks/Time.qml
@@ -0,0 +1,10 @@
+import QtQuick
+import "../"
+
+BarBlock {
+ id: text
+ content: BarText {
+ symbolText: ` ${Datetime.time}`
+ }
+}
+
diff --git a/home/quickshell/bar/blocks/Workspace.qml b/home/quickshell/bar/blocks/Workspace.qml
new file mode 100644
index 0000000..232a3f3
--- /dev/null
+++ b/home/quickshell/bar/blocks/Workspace.qml
@@ -0,0 +1,28 @@
+import QtQuick
+import QtQuick.Layouts
+
+Rectangle {
+ id: ws
+
+ property bool hovered: false
+
+ Layout.preferredWidth: 10
+ Layout.preferredHeight: 10
+ Layout.minimumWidth: 10
+ Layout.minimumHeight: 10
+ Layout.alignment: Qt.AlignHCenter
+ radius: height / 2
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+
+ onEntered: () => {
+ ws.hovered = true;
+ }
+ onExited: () => {
+ ws.hovered = false;
+ }
+ onClicked: () => console.log(`workspace ?`)
+ }
+}
diff --git a/home/quickshell/bar/blocks/Workspaces.qml b/home/quickshell/bar/blocks/Workspaces.qml
new file mode 100644
index 0000000..66d48a6
--- /dev/null
+++ b/home/quickshell/bar/blocks/Workspaces.qml
@@ -0,0 +1,74 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Hyprland
+import Quickshell.Widgets
+import Qt5Compat.GraphicalEffects
+import "../utils" as Utils
+import "root:/"
+
+RowLayout {
+ property HyprlandMonitor monitor: Hyprland.monitorFor(screen)
+
+ Rectangle {
+ id: workspaceBar
+ Layout.preferredWidth: Math.max(50, Utils.HyprlandUtils.maxWorkspace * 25)
+ Layout.preferredHeight: 23
+ radius: 7
+ color: Theme.get.barBgColor
+
+ Row {
+ anchors.centerIn: parent
+ spacing: 15
+
+ Repeater {
+ model: Utils.HyprlandUtils.maxWorkspace || 1
+
+ Item {
+ required property int index
+ property bool focused: Hyprland.focusedMonitor?.activeWorkspace?.id === (index + 1)
+
+ width: workspaceText.width
+ height: workspaceText.height
+
+ Text {
+ id: workspaceText
+ text: (index + 1).toString()
+ color: "white"
+ font.pixelSize: 15
+ font.bold: focused
+ }
+
+ Rectangle {
+ visible: focused
+ anchors {
+ left: workspaceText.left
+ right: workspaceText.right
+ top: workspaceText.bottom
+ topMargin: -3
+ }
+ height: 2
+ color: "white"
+ }
+
+ DropShadow {
+ visible: focused
+ anchors.fill: workspaceText
+ horizontalOffset: 2
+ verticalOffset: 2
+ radius: 8.0
+ samples: 20
+ color: "#000000"
+ source: workspaceText
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: Utils.HyprlandUtils.switchWorkspace(index + 1)
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/home/quickshell/bar/utils/HyprlandUtils.qml b/home/quickshell/bar/utils/HyprlandUtils.qml
new file mode 100644
index 0000000..dde3b31
--- /dev/null
+++ b/home/quickshell/bar/utils/HyprlandUtils.qml
@@ -0,0 +1,54 @@
+pragma Singleton
+
+import Quickshell
+import Quickshell.Hyprland
+import QtQuick
+
+Singleton {
+ id: hyprland
+
+ property list<HyprlandWorkspace> workspaces: sortWorkspaces(Hyprland.workspaces.values)
+ property int maxWorkspace: findMaxId()
+
+ function sortWorkspaces(ws) {
+ return [...ws].sort((a, b) => a?.id - b?.id);
+ }
+
+ function switchWorkspace(w: int): void {
+ Hyprland.dispatch(`workspace ${w}`);
+ }
+
+ function findMaxId(): int {
+ if (hyprland.workspaces.length === 0) {
+ console.log("No workspaces found, defaulting to 1");
+ return 1; // Return 1 if no workspaces exist
+ }
+ let num = hyprland.workspaces.length;
+ let maxId = hyprland.workspaces[num - 1]?.id || 1;
+ console.log("Current max workspace ID:", maxId);
+ return maxId;
+ }
+
+ Connections {
+ target: Hyprland
+ function onRawEvent(event) {
+ let eventName = event.name;
+ console.log("Hyprland event received:", eventName);
+
+ switch (eventName) {
+ case "createworkspacev2":
+ {
+ console.log("Workspace created, updating workspace list");
+ hyprland.workspaces = hyprland.sortWorkspaces(Hyprland.workspaces.values);
+ hyprland.maxWorkspace = findMaxId();
+ }
+ case "destroyworkspacev2":
+ {
+ console.log("Workspace destroyed, updating workspace list");
+ hyprland.workspaces = hyprland.sortWorkspaces(Hyprland.workspaces.values);
+ hyprland.maxWorkspace = findMaxId();
+ }
+ }
+ }
+ }
+}
diff --git a/home/quickshell/default.nix b/home/quickshell/default.nix
new file mode 100644
index 0000000..43f7d76
--- /dev/null
+++ b/home/quickshell/default.nix
@@ -0,0 +1,20 @@
+{
+ pkgs,
+ pkgs-unstable,
+ lib,
+ config,
+ ...
+}:
+{
+ imports = [ ./quickshell.nix ];
+ programs.quickshell = {
+ enable = true;
+ systemd.enable = true;
+ package = pkgs-unstable.quickshell;
+ systemd.target = "hyprland-session.target";
+ };
+ xdg.configFile."quickshell/shell.qml".source = ./shell.qml;
+ xdg.configFile."quickshell/Theme.qml".source = ./Theme.qml;
+ xdg.configFile."quickshell/PopupContext.qml".source = ./PopupContext.qml;
+ xdg.configFile."quickshell/bar".source = ./bar;
+}
diff --git a/home/quickshell/quickshell.nix b/home/quickshell/quickshell.nix
new file mode 100644
index 0000000..87cc52c
--- /dev/null
+++ b/home/quickshell/quickshell.nix
@@ -0,0 +1,90 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.programs.quickshell;
+in
+{
+ meta.maintainers = [ lib.maintainers.justdeeevin ];
+
+ options.programs.quickshell = {
+ enable = lib.mkEnableOption "quickshell, a flexbile QtQuick-based desktop shell toolkit.";
+ package = lib.mkPackageOption pkgs "quickshell" { nullable = true; };
+ configs = lib.mkOption {
+ type = lib.types.attrsOf lib.types.path;
+ default = { };
+ description = ''
+ A set of configs to include in the quickshell config directory. The key is the name of the config.
+
+ The configuration that quickshell should use can be specified with the `activeConfig` option.
+ '';
+ };
+ activeConfig = lib.mkOption {
+ type = lib.types.nullOr lib.types.str;
+ default = null;
+ description = ''
+ The name of the config to use.
+
+ If `null`, quickshell will attempt to use a config located in `$XDG_CONFIG_HOME/quickshell` instead of one of the named sub-directories.
+ '';
+ };
+
+ systemd = {
+ enable = lib.mkEnableOption "quickshell systemd service";
+ target = lib.mkOption {
+ type = lib.types.str;
+ default = config.wayland.systemd.target;
+ defaultText = lib.literalExpression "config.wayland.systemd.target";
+ example = "hyprland-session.target";
+ description = ''
+ The systemd target that will automatically start quickshell.
+
+ If you set this to a WM-specific target, make sure that systemd integration for that WM is enabled (e.g. `wayland.windowManager.hyprland.systemd.enable`). **This is typically true by default**.
+ '';
+ };
+ };
+ };
+
+ config = lib.mkIf cfg.enable (
+ lib.mkMerge [
+ (lib.mkIf (cfg.configs != { }) {
+ xdg.configFile = lib.mapAttrs' (name: path: {
+ name = "quickshell/${name}";
+ value.source = path;
+ }) cfg.configs;
+ })
+ {
+ assertions = [
+ (lib.hm.assertions.assertPlatform "programs.quickshell" pkgs lib.platforms.linux)
+ {
+ assertion = !(builtins.any (name: lib.hasInfix "/" name) (builtins.attrNames cfg.configs));
+ message = "The names of configs in `programs.quickshell.configs` must not contain slashes.";
+ }
+ ];
+
+ home.packages = [ cfg.package ];
+
+ }
+ (lib.mkIf cfg.systemd.enable {
+ systemd.user.services.quickshell = {
+ Unit = {
+ Description = "quickshell";
+ Documentation = "https://quickshell.outfoxxed.me/docs/";
+ After = [ cfg.systemd.target ];
+ };
+
+ Service = {
+ ExecStart =
+ lib.getExe cfg.package + (if cfg.activeConfig == null then "" else " --config ${cfg.activeConfig}");
+ Restart = "on-failure";
+ };
+
+ Install.WantedBy = [ cfg.systemd.target ];
+ };
+ })
+ ]
+ );
+}
diff --git a/home/quickshell/shell.qml b/home/quickshell/shell.qml
new file mode 100644
index 0000000..43bb3e9
--- /dev/null
+++ b/home/quickshell/shell.qml
@@ -0,0 +1,8 @@
+//@ pragma UseQApplication
+import "bar"
+
+import Quickshell
+
+ShellRoot {
+ Bar {}
+}
diff --git a/home/tenere/default.nix b/home/tenere/default.nix
new file mode 100644
index 0000000..f7cfa29
--- /dev/null
+++ b/home/tenere/default.nix
@@ -0,0 +1,16 @@
+{
+ config,
+ inputs,
+ pkgs,
+ ...
+}:
+{
+ xdg.configFile."/tenere/config.toml".text = ''
+ llm = "chatgpt"
+
+ [chatgpt]
+ openai_api_key = $(${pkgs.coreutils}/bin/cat ${config.age.secrets.openai_auth_token.path})
+ model = "gpt-4.1"
+ url = "https://api.openai.com/v1/chat/completions"
+ '';
+}
diff --git a/home/ticker/default.nix b/home/ticker/default.nix
new file mode 100644
index 0000000..be2f509
--- /dev/null
+++ b/home/ticker/default.nix
@@ -0,0 +1,41 @@
+{ inputs, pkgs, ... }:
+{
+ xdg.configFile."/home/petri/.config/ticker/config.yaml".text = ''
+ show-summary: true
+ show-tags: true
+ show-fundamentals: true
+ show-separator: true
+ show-holdings: true
+ interval: 5
+ currency: EUR
+ currency-summary-only: false
+ watchlist:
+ - NET
+ - TEAM
+ - ESTC
+ - BTC-USD # Bitcoin price via Yahoo
+ - SOL.X # Solana price via CoinGecko
+ - SAMOYEDCOIN.CG # Samoyed price via CoinGecko
+ - CARDANO.CC # Samoyed price via CoinCap
+ lots:
+ - symbol: "ABNB"
+ quantity: 35.0
+ unit_cost: 146.00
+ - symbol: "ARKW"
+ quantity: 20.0
+ unit_cost: 152.25
+ - symbol: "ARKW"
+ quantity: 20.0
+ unit_cost: 145.35
+ fixed_cost: 7.00 # e.g. brokerage commission fee
+ groups:
+ - name: crypto
+ watchlist:
+ - SHIB-USD
+ - VGX-USD
+ holdings:
+ - symbol: SOL1-USD
+ quantity: 17
+ unit_cost: 159.10
+ '';
+}
diff --git a/home/vale/default.nix b/home/vale/default.nix
new file mode 100644
index 0000000..79712ea
--- /dev/null
+++ b/home/vale/default.nix
@@ -0,0 +1,31 @@
+{ inputs, pkgs, ... }:
+{
+
+ xdg.configFile."/home/petri/.config/vale/.vale.ini".text = ''
+ MinAlertLevel = suggestion
+ StylesPath = ${pkgs.valeStyles.microsoft}/share/vale/styles
+ Packages = Microsoft
+ # Vocab = myterms does not work because expects styles to be in the same folder
+
+ [asciidoctor]
+ experimental = YES
+ attribute-missing = drop
+
+ [*.adoc]
+ BasedOnStyles = Vale, Microsoft
+
+ [*.{md,rst}]
+ BasedOnStyles = Vale, Microsoft
+ '';
+
+ xdg.configFile."/home/petri/.config/vale/config/vocabularies/myterms/accept.txt".text = ''
+ Relesoft
+ ERTMS
+ ETCS
+ YAML
+ TOML
+ FTIA
+ GRK
+ EULYNX
+ '';
+}
diff --git a/home/walker/config.toml b/home/walker/config.toml
new file mode 100644
index 0000000..5c2b1ef
--- /dev/null
+++ b/home/walker/config.toml
@@ -0,0 +1,251 @@
+app_launch_prefix = ""
+terminal_title_flag = ""
+locale = ""
+close_when_open = false
+theme = "default"
+monitor = ""
+hotreload_theme = false
+as_window = false
+timeout = 0
+disable_click_to_close = false
+force_keyboard_focus = false
+
+[keys]
+accept_typeahead = ["tab"]
+trigger_labels = "lalt"
+next = ["down"]
+prev = ["up"]
+close = ["esc"]
+remove_from_history = ["shift backspace"]
+resume_query = ["ctrl r"]
+toggle_exact_search = ["ctrl m"]
+
+[keys.activation_modifiers]
+keep_open = "shift"
+alternate = "alt"
+
+[keys.ai]
+clear_session = ["ctrl x"]
+copy_last_response = ["ctrl c"]
+resume_session = ["ctrl r"]
+run_last_response = ["ctrl e"]
+
+[events]
+on_activate = ""
+on_selection = ""
+on_exit = ""
+on_launch = ""
+on_query_change = ""
+
+[list]
+dynamic_sub = true
+keyboard_scroll_style = "emacs"
+max_entries = 50
+show_initial_entries = true
+single_click = true
+visibility_threshold = 20
+placeholder = "No Results"
+
+[search]
+argument_delimiter = "#"
+placeholder = "Search..."
+delay = 0
+resume_last_query = false
+
+[activation_mode]
+labels = "jkl;asdf"
+
+[builtins.applications]
+weight = 5
+name = "applications"
+placeholder = "Applications"
+prioritize_new = true
+hide_actions_with_empty_query = true
+context_aware = true
+refresh = true
+show_sub_when_single = true
+show_icon_when_single = true
+show_generic = true
+history = true
+
+[builtins.applications.actions]
+enabled = true
+hide_category = false
+hide_without_query = true
+
+[builtins.bookmarks]
+weight = 5
+placeholder = "Bookmarks"
+name = "bookmarks"
+icon = "bookmark"
+switcher_only = true
+
+[[builtins.bookmarks.entries]]
+label = "Walker"
+url = "https://github.com/abenz1267/walker"
+keywords = ["walker", "github"]
+
+[builtins.xdph_picker]
+hidden = true
+weight = 5
+placeholder = "Screen/Window Picker"
+show_sub_when_single = true
+name = "xdphpicker"
+switcher_only = true
+
+[builtins.ai]
+weight = 5
+placeholder = "AI"
+name = "ai"
+icon = "help-browser"
+switcher_only = true
+show_sub_when_single = true
+
+[[builtins.ai.anthropic.prompts]]
+model = "claude-3-7-sonnet-20250219"
+temperature = 1
+max_tokens = 1_000
+label = "General Assistant"
+prompt = "You are a helpful general assistant. Keep your answers short and precise."
+
+[builtins.calc]
+require_number = true
+weight = 5
+name = "calc"
+icon = "accessories-calculator"
+placeholder = "Calculator"
+min_chars = 4
+
+[builtins.windows]
+weight = 5
+icon = "view-restore"
+name = "windows"
+placeholder = "Windows"
+show_icon_when_single = true
+
+[builtins.clipboard]
+always_put_new_on_top = true
+exec = "wl-copy"
+weight = 5
+name = "clipboard"
+avoid_line_breaks = true
+placeholder = "Clipboard"
+image_height = 300
+max_entries = 10
+switcher_only = true
+
+[builtins.commands]
+weight = 5
+icon = "utilities-terminal"
+switcher_only = true
+name = "commands"
+placeholder = "Commands"
+
+[builtins.custom_commands]
+weight = 5
+icon = "utilities-terminal"
+name = "custom_commands"
+placeholder = "Custom Commands"
+
+[builtins.emojis]
+exec = "wl-copy"
+weight = 5
+name = "emojis"
+placeholder = "Emojis"
+switcher_only = true
+history = true
+typeahead = true
+show_unqualified = false
+
+[builtins.symbols]
+after_copy = ""
+weight = 5
+name = "symbols"
+placeholder = "Symbols"
+switcher_only = true
+history = true
+typeahead = true
+
+[builtins.finder]
+use_fd = false
+fd_flags = "--ignore-vcs --type file"
+weight = 5
+icon = "file"
+name = "finder"
+placeholder = "Finder"
+switcher_only = true
+ignore_gitignore = true
+refresh = true
+concurrency = 8
+show_icon_when_single = true
+preview_images = false
+
+[builtins.runner]
+eager_loading = true
+weight = 5
+icon = "utilities-terminal"
+name = "runner"
+placeholder = "Runner"
+typeahead = true
+history = true
+generic_entry = false
+refresh = true
+use_fd = false
+
+[builtins.ssh]
+weight = 5
+icon = "preferences-system-network"
+name = "ssh"
+placeholder = "SSH"
+switcher_only = true
+history = true
+refresh = true
+
+[builtins.switcher]
+weight = 5
+name = "switcher"
+placeholder = "Switcher"
+prefix = "/"
+
+[builtins.websearch]
+keep_selection = true
+weight = 5
+icon = "applications-internet"
+name = "websearch"
+placeholder = "Websearch"
+
+[[builtins.websearch.entries]]
+name = "Google"
+url = "https://www.google.com/search?q=%TERM%"
+
+[[builtins.websearch.entries]]
+name = "DuckDuckGo"
+url = "https://duckduckgo.com/?q=%TERM%"
+switcher_only = true
+
+[[builtins.websearch.entries]]
+name = "Ecosia"
+url = "https://www.ecosia.org/search?q=%TERM%"
+switcher_only = true
+
+[[builtins.websearch.entries]]
+name = "Yandex"
+url = "https://yandex.com/search/?text=%TERM%"
+switcher_only = true
+
+[builtins.dmenu]
+hidden = true
+weight = 5
+name = "dmenu"
+placeholder = "Dmenu"
+switcher_only = true
+show_icon_when_single = true
+
+[builtins.translation]
+delay = 1000
+weight = 5
+name = "translation"
+icon = "accessories-dictionary"
+placeholder = "Translation"
+switcher_only = true
+provider = "googlefree"
diff --git a/home/walker/default.nix b/home/walker/default.nix
new file mode 100644
index 0000000..e28780f
--- /dev/null
+++ b/home/walker/default.nix
@@ -0,0 +1,9 @@
+{ config, pkgs, ... }:
+
+{
+ home.file = {
+ "/home/petri/.config/walker/config.toml" = {
+ source = ./config.toml;
+ };
+ };
+}
diff --git a/home/wallpapers/crow.jpg b/home/wallpapers/crow.jpg
new file mode 100644
index 0000000..7eec09d
--- /dev/null
+++ b/home/wallpapers/crow.jpg
Binary files differ
diff --git a/home/wallpapers/crow2.avif b/home/wallpapers/crow2.avif
new file mode 100644
index 0000000..6353b15
--- /dev/null
+++ b/home/wallpapers/crow2.avif
Binary files differ
diff --git a/home/wallpapers/default.nix b/home/wallpapers/default.nix
new file mode 100644
index 0000000..458f711
--- /dev/null
+++ b/home/wallpapers/default.nix
@@ -0,0 +1,87 @@
+{ config, pkgs, ... }:
+let
+ wallpaperScript = pkgs.writeShellScript "update-wallpaper.sh" ''
+ set -euo pipefail
+
+ if [ -z "''${UNSPLASH_ACCESS_KEY:-}" ]; then
+ echo "Error: UNSPLASH_ACCESS_KEY is not set"
+ exit 1
+ fi
+
+ WALLPAPER_DIR="$HOME/.local/share/wallpapers"
+ "${pkgs.coreutils}/bin/mkdir" -p "$WALLPAPER_DIR"
+
+ TMPFILE="$("${pkgs.coreutils}/bin/mktemp")"
+
+ echo "Fetching random Unsplash image URL..."
+ URL=$("${pkgs.curl}/bin/curl" -s \
+ "https://api.unsplash.com/photos/random?query=nature,landscape&orientation=landscape&client_id=$UNSPLASH_ACCESS_KEY" \
+ | "${pkgs.jq}/bin/jq" -r '.urls.raw')
+
+ if [ -z "$URL" ] || [ "$URL" = "null" ]; then
+ echo "Error: could not retrieve Unsplash URL"
+ exit 1
+ fi
+
+ EXT=$("${pkgs.coreutils}/bin/basename" "$URL" | "${pkgs.gawk}/bin/awk" -F. '{print tolower($NF)}')
+ [[ "$EXT" =~ ^(jpg|jpeg|png)$ ]] || EXT="jpg"
+
+ TMPFILE_EXT="$TMPFILE.$EXT"
+ OUTFILE="$WALLPAPER_DIR/$("${pkgs.coreutils}/bin/date" +%Y%m%d-%H%M%S).webp"
+
+ echo "Downloading original Unsplash image..."
+ "${pkgs.curl}/bin/curl" -L --fail --retry 3 -o "$TMPFILE_EXT" "$URL"
+
+ echo "Converting to WebP..."
+ "${pkgs.libwebp}/bin/cwebp" -mt -preset photo -hint photo -metadata all -pass 10 -quiet -q 80 -m 6 "$TMPFILE_EXT" -o "$OUTFILE"
+ "${pkgs.coreutils}/bin/rm" -f "$TMPFILE_EXT"
+
+ echo "Selecting a wallpaper different from current..."
+ CURRENT_WALL=$("${pkgs.hyprland}/bin/hyprctl" hyprpaper listloaded | "${pkgs.gawk}/bin/awk" '{print $NF}' || true)
+ NEW_WALL=$("${pkgs.findutils}/bin/find" "$WALLPAPER_DIR" -type f ! -name "$("${pkgs.coreutils}/bin/basename" "$CURRENT_WALL")" | "${pkgs.coreutils}/bin/shuf" -n 1)
+
+ echo "Reloading hyprpaper with: $NEW_WALL"
+ "${pkgs.hyprland}/bin/hyprctl" hyprpaper reload ,"$NEW_WALL"
+ '';
+in
+{
+ # Ensure required packages are available
+ home.packages = with pkgs; [
+ coreutils
+ curl
+ findutils
+ hyprland
+ jq
+ libwebp
+ ];
+
+ systemd.user.services."wallpaper-fetch" = {
+ Unit = {
+ Description = "Fetch and update 4K nature wallpaper for Hyprpaper";
+ After = [ "graphical-session.target" ];
+ };
+
+ Service = {
+ Environment = [
+ "UNSPLASH_ACCESS_KEY=$(${pkgs.coreutils}/bin/cat ${config.age.secrets.unsplash_access_key.path})"
+ ];
+ Type = "oneshot";
+ ExecStart = "${wallpaperScript}";
+ };
+ };
+
+ # Timer to run periodically (every 3 hours)
+ systemd.user.timers."wallpaper-fetch" = {
+ Unit = {
+ Description = "Periodic Unsplash wallpaper fetch timer";
+ };
+ Timer = {
+ OnBootSec = "2min";
+ OnUnitActiveSec = "3h";
+ Persistent = true;
+ };
+ Install = {
+ WantedBy = [ "timers.target" ];
+ };
+ };
+}
diff --git a/home/wallpapers/owl1.webp b/home/wallpapers/owl1.webp
new file mode 100644
index 0000000..f3c64d1
--- /dev/null
+++ b/home/wallpapers/owl1.webp
Binary files differ
diff --git a/home/wallpapers/owl2.webp b/home/wallpapers/owl2.webp
new file mode 100644
index 0000000..fa053a5
--- /dev/null
+++ b/home/wallpapers/owl2.webp
Binary files differ
diff --git a/home/wallpapers/owl3.webp b/home/wallpapers/owl3.webp
new file mode 100644
index 0000000..8861509
--- /dev/null
+++ b/home/wallpapers/owl3.webp
Binary files differ
diff --git a/home/waybar/config b/home/waybar/config
deleted file mode 100644
index 779a9a1..0000000
--- a/home/waybar/config
+++ /dev/null
@@ -1,160 +0,0 @@
-{
- // -------------------------------------------------------------------------
- // Global configuration
- // -------------------------------------------------------------------------
- "ipc": true,
- "position": "bottom",
- "layer": "top",
- "height": 30,
- "mode": "dock",
- "modules-left": [
- "hyprland/hide",
- "hyprland/mode",
- "hyprland/workspaces",
- "disk",
- "memory",
- "cpu",
- "temperature",
- "custom/spotify"
- ],
- "modules-center": [
- "hyprland/window"
- ],
- "modules-right": [
- "pulseaudio",
- "battery",
- "backlight",
- "clock#date",
- "clock#time",
- "network",
- "tray"
- ],
- // -------------------------------------------------------------------------
- // Modules
- // -------------------------------------------------------------------------
- "hyprland/hide": {
- "hide-on-startup": true
- },
- "battery": {
- "interval": 1,
- "states": {
- "low": 30,
- "warning": 15,
- "critical": 5
- },
- "format": "{icon} {capacity}%",
- "format-discharging": "{icon} {capacity}%",
- "format-charging": "󰂄 {capacity}%",
- "tooltip-format": "{time} left",
- "format-icons": [
- "󰁺",
- "󰁻",
- "󰁼",
- "󰁽",
- "󰁾",
- "󰁿",
- "󰂀",
- "󰂁",
- "󰂂",
- "󰁹",
- ],
- "tooltip": true,
- "full-at": 95
- },
- "clock#time": {
- "interval": 10,
- "format": "{:%H:%M}",
- "tooltip": false
- },
- "clock#date": {
- "interval": 60,
- "format": "{:%A, %e %b %Y}",
- "tooltip": true
- },
- "cpu": {
- "interval": 2,
- "tooltip": true,
- "format": " {usage}%",
- "states": {
- "low": 0,
- "normal": 20,
- "warning": 70,
- "critical": 90
- }
- },
- "memory": {
- "interval": 5,
- "format": " {used:0.1f}G",
- "tooltip-format": "{used:0.1f}G / {total:0.1f}G",
- "states": {
- "warning": 70,
- "critical": 90
- }
- },
- "network": {
- "interval": 1,
- "format-wifi": "{icon}",
- "format-disconnected": "󱖣",
- "format-icons": [
- "",
- "",
- "",
- "",
- ""
- ],
- "tooltip-format": "{essid} ({signalStrength}%) - {ifname} - {ipaddr} - {bandwidthDownBits} {bandwidthUpBits}",
- "tooltip": true,
- },
- "hyprland/mode": {
- "format": "<span style=\"italic\"> {}</span>",
- "tooltip": false
- },
- "hyprland/window": {
- "format": "{}",
- "max-length": 60,
- "tooltip": true
- },
- "hyprland/workspaces": {
- "all-outputs": true,
- "format": "{icon}"
- },
- "pulseaudio": {
- "scroll-step": 1,
- "format": "{icon} {volume}%",
- "format-muted": "",
- "format-bluetooth": " {icon} {volume}%",
- "format-bluetooth-muted": "",
- "on-click": "pulseaudio-control togmute",
- "on-click-middle": "pulseaudio-control --sink-blacklist 'alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_3__sink,alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_4__sink,alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_5__sink' next-sink"
- },
- "temperature": {
- "critical-threshold": 90,
- "interval": 5,
- "format": "{icon} {temperatureC}°C",
- "format-icons": [
- "",
- "",
- "",
- "",
- ""
- ],
- "tooltip": true
- },
- "disk": {
- "interval": 10,
- "format": "{free}"
- },
- "backlight": {
- "device": "intel_backlight",
- "format": "{icon} {percent}",
- "format-icons": [
- ""
- ],
- "on-scroll-up": "brightnessctl set +1% -e 6 -n 1",
- "on-scroll-down": "brightnessctl set 1%- -e 6 -n 1"
- },
- "tray": {
- "icon-size": 24,
- "spacing": 10
- }
-}
diff --git a/home/waybar/default.nix b/home/waybar/default.nix
index bfe3c39..fd9a735 100644
--- a/home/waybar/default.nix
+++ b/home/waybar/default.nix
@@ -1,6 +1,304 @@
+{ pkgs, lib, ... }:
+
+let
+ waybarTimer = pkgs.writeShellScript "waybar_timer.sh" ''
+ #!/bin/sh
+ CUR_TIME=$(date +%s)
+ STATUS=$(cat /var/tmp/waybar_timer)
+
+ if [ $STATUS == "READY" ]; then
+ echo ""
+ elif [ $STATUS == "FINISHED" ]; then
+ mpv $HOME/.config/waybar/modules/timer.wav &
+ echo "READY" > /var/tmp/waybar_timer
+ echo ""
+ elif [[ $STATUS > $CUR_TIME ]]; then
+ DIFF=$(( (STATUS - CUR_TIME) ))
+ echo $( date -d @$DIFF +%M:%S )
+ else
+ if [ -f "/var/tmp/waybar_timer" ]; then
+ echo "FINISHED" > /var/tmp/waybar_timer
+ echo ""
+ else
+ echo "READY" > /var/tmp/waybar_timer
+ echo ""
+ fi
+ fi
+ '';
+in
+
{
- programs.waybar = { enable = true; };
+ programs.waybar = {
+ systemd.enable = true;
+ enable = true;
+ settings = [
+ {
+ ipc = true;
+ layer = "top";
+ position = "top";
+ height = 36;
+ margin = "5 5 0 5";
+ modules-left = [
+ "custom/startmenu"
+ "hyprland/workspaces"
+ "idle_inhibitor"
+ "keyboard-state"
+ "hyprland/language"
+ "systemd-failed-units"
+ "wireplumber"
+ "privacy"
+ "hyprland/window"
+ ];
+ modules-center = [
+ "clock"
+ "custom/timer"
+ "mpris"
+ ];
+ modules-right = [
+ "network"
+ "temperature"
+ "load"
+ "group/system-status"
+ "bluetooth"
+ "tray"
+ "custom/weather"
+ "custom/exit"
+ ];
+
+ "hyprland/workspaces" = {
+ format = "{name}";
+ format-icons = {
+ default = " ";
+ active = " ";
+ urgent = " ";
+ };
+ on-scroll-up = "${pkgs.hyprland}/bin/hyprctl dispatch workspace e+1";
+ on-scroll-down = "${pkgs.hyprland}/bin/hyprctl dispatch workspace e-1";
+ };
+
+ clock = {
+ interval = 1;
+ format = "{:%A, %Y-%m-%d %H:%M:%S}";
+ tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
+ calendar = {
+ mode = "year";
+ weeks-pos = "right";
+ mode-mon-col = 3;
+ format = {
+ months = "<span color='#ffead3'><b>{}</b></span>";
+ days = "<span color='#ecc6d9'><b>{}</b></span>";
+ weeks = "<span color='#99ffdd'><b>W{}</b></span>";
+ weekdays = "<span color='#ffcc66'><b>{}</b></span>";
+ today = "<span color='#ff6699'><b><u>{}</u></b></span>";
+ };
+ };
+ };
+
+ "hyprland/window" = {
+ max-length = 80;
+ separate-outputs = false;
+ rewrite = {
+ "" = "🙈 No Windows?";
+ };
+ };
+
+ "hyprland/language" = {
+ format = "{long}";
+ min-length = 14;
+ };
+
+ "keyboard-state" = {
+ capslock = true;
+ format = "{icon}";
+ format-icons = {
+ locked = "";
+ unlocked = "";
+ };
+ tooltip = false;
+ };
+
+ "custom/weather" = {
+ format = "{}°C";
+ tooltip = true;
+ interval = 3600;
+ exec = "${pkgs.wttrbar}/bin/wttrbar --date-format=%d.%m.%Y --location=Helsinki";
+ return-type = "json";
+ };
+
+ "group/system-status" = {
+ orientation = "horizontal";
+ modules = [
+ "cpu"
+ "memory"
+ "battery"
+ "backlight"
+ ];
+ };
+ memory = {
+ interval = 1;
+ format = " {}%";
+ tooltip-format = "{used:0.1f}G / {total:0.1f}G";
+ states = {
+ warning = 70;
+ critical = 90;
+ };
+ };
+
+ load = {
+ interval = 1;
+ format = "load: {load1:>4}";
+ max-length = 10;
+ };
+
+ cpu = {
+ interval = 1;
+ format = " {usage:>3}%";
+ tooltip-format = "{usage}%";
+ tooltip = true;
+ states = {
+ warning = 70;
+ critical = 90;
+ };
+ };
+
+ backlight = {
+ device = "intel_backlight";
+ format = "{icon} {percent}%";
+ format-icons = [ "" ];
+ on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl set +1%";
+ on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl set 1%-";
+ };
+
+ systemd-failed-units = {
+ hide-on-ok = false;
+ format = "✗ {nr_failed}";
+ format-ok = "✓";
+ system = true;
+ };
+
+ temperature = {
+ format = "{temperatureC}°C ";
+ };
+
+ privacy = {
+ icon-spacing = 4;
+ icon-size = 18;
+ transition-duration = 250;
+ modules = [
+ {
+ type = "screenshare";
+ tooltip = true;
+ tooltip-icon-size = 24;
+ }
+ {
+ type = "audio-out";
+ tooltip = true;
+ tooltip-icon-size = 24;
+ }
+ {
+ type = "audio-in";
+ tooltip = true;
+ tooltip-icon-size = 24;
+ }
+ ];
+ ignore-monitor = true;
+ };
+
+ network = {
+ tooltip = true;
+ interval = 1;
+ format-wifi = "{icon} {essid} ({signalStrength}) up: {bandwidthUpBits:>6} down: {bandwidthDownBits:>6}";
+ format-ethernet = "{icon} up: {bandwidthUpBits:>6} down: {bandwidthDownBits:>6}";
+ format-disconnected = "⚠ Disconnected";
+ tooltip-format = "{ifname}: {ipaddr}/{cidr}\nUp: {bandwidthUpBits:>6}\nDown: {bandwidthDownBits:>6}";
+ format-icons = {
+ wifi = "";
+ ethernet = "";
+ };
+ };
+ tray = {
+ spacing = 12;
+ };
+ bluetooth = {
+ format = " {status}";
+ format-connected = " {device_alias}";
+ format-connected-battery = " {device_alias} {device_battery_percentage}%";
+ tooltip-format = "{controller_alias}\t{controller_address}\n\n{num_connections} connected";
+ tooltip-format-connected = "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}";
+ tooltip-format-enumerate-connected = "{device_alias}\t{device_address}";
+ tooltip-format-enumerate-connected-battery = "{device_alias}\t{device_address}\t{device_battery_percentage}%";
+ };
+ mpris = {
+ format = "DEFAULT: {player_icon} {dynamic}";
+ format-paused = "DEFAULT: {status_icon} <i>{dynamic}</i>";
+ interval = 1;
+ player-icons = {
+ default = "▶";
+ mpv = "🎵";
+ };
+ status-icons = {
+ paused = "⏸";
+ };
+ };
+ wireplumber = {
+ format = "{icon} {volume}%";
+ format-muted = " {volume}%";
+ format-icons = {
+ default = [
+ ""
+ ""
+ ""
+ ];
+ };
+ on-click = "${pkgs.lxqt.pavucontrol-qt}/bin/pavucontrol-qt";
+ };
+
+ "custom/timer" = {
+ tooltip = false;
+ min-length = 5;
+ exec = "${waybarTimer}";
+ on-click = "date --date='5 minutes' +%s > /var/tmp/waybar_timer";
+ on-click-right = "echo READY > /var/tmp/waybar_timer";
+ on-click-middle = "date --date='1 minute' +%s > /var/tmp/waybar_timer";
+ on-scroll-up = "date --date='5 seconds' +%s > /var/tmp/waybar_timer";
+ on-scroll-down = "date --date='30 minutes' +%s > /var/tmp/waybar_timer";
+ interval = 1;
+ };
+
+ "custom/exit" = {
+ format = "";
+ on-click = "${pkgs.wlogout}/bin/wlogout";
+ };
+
+ "custom/startmenu" = {
+ format = "⚡";
+ on-click = "${pkgs.walker}/bin/walker";
+ };
- home.file."./config/waybar/config".source = ./config;
- home.file."./config/waybar/style.css".source = ./style.css;
+ "idle_inhibitor" = {
+ format = "{icon}";
+ format-icons = {
+ activated = "";
+ deactivated = "";
+ };
+ };
+ battery = {
+ states = {
+ warning = 30;
+ critical = 15;
+ };
+ format = "{icon} {capacity}%";
+ format-charging = " {capacity}%";
+ format-plugged = " {capacity}%";
+ format-full = " {capacity}%";
+ format-icons = [
+ ""
+ ""
+ ""
+ ];
+ };
+ }
+ ];
+ style = builtins.readFile ./style.css;
+ };
}
diff --git a/home/waybar/style.css b/home/waybar/style.css
index 8fe60c7..7e6e821 100644
--- a/home/waybar/style.css
+++ b/home/waybar/style.css
@@ -1,258 +1,140 @@
-@keyframes blink-low {
- 70% {
- color: @foreground;
- }
-
- to {
- color: @foreground;
- background-color: #744E0D;
- }
-}
-
-@keyframes blink-warning {
- 70% {
- color: @foreground;
- }
-
- to {
- color: @foreground;
- background-color: @warning;
- }
-}
-
-@keyframes blink-critical {
- 70% {
- color: @foreground;
- }
-
- to {
- color: @foreground;
- background-color: @critical;
- }
-}
-
-
-/* -----------------------------------------------------------------------------
- * Styles
- * -------------------------------------------------------------------------- */
-
-/* Nord */
-/* Gruvbox */
-@define-color foreground #D4BE98;
-@define-color background #202020;
-@define-color backgroundlight #2A2A2A;
-@define-color mode #a89984;
-@define-color workspaces #458588;
-@define-color workspacesfocused #83a598;
-@define-color sound #E78A4E;
-@define-color network #A9B665;
-@define-color memory #7DAEA3;
-@define-color cpu #8EC07C;
-@define-color layout #689d6a;
-@define-color battery #A9B665;
-@define-color date #282828;
-@define-color time #ebdbb2;
-@define-color warning #DE655E;
-@define-color critical #FB4934;
-@define-color temp #A9B665;
-
-/* Reset all styles */
+/* Nord Theme Colors */
+@define-color nord0 #2e3440;
+@define-color nord1 #3b4252;
+@define-color nord2 #434c5e;
+@define-color nord3 #4c566a;
+@define-color nord4 #d8dee9;
+@define-color nord5 #e5e9f0;
+@define-color nord6 #eceff4;
+@define-color nord7 #8fbcbb;
+@define-color nord8 #88c0d0;
+@define-color nord9 #81a1c1;
+@define-color nord10 #5e81ac;
+@define-color nord11 #bf616a;
+@define-color nord13 #ebcb8b;
+
+/* Base styles */
* {
- border: none;
- border-radius: 0;
- min-height: 0;
- margin: 0;
- padding: 0;
+ font-family: 'Iosevka Nerd Font', monospace;
+ font-size: 18px;
+ border: none;
+ border-radius: 8px;
+ margin: 0;
+ padding: 0;
+ min-height: 0;
+ transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, opacity 0.2s ease-in-out;
}
-/* The whole bar */
#waybar {
- background: @background;
- color: @foreground;
- font-family: Sarasa Term K, JetBrainsMono Nerd Font, Material Design Icons, unifont, Fira Code, Terminus, Siji;
- font-size: 12pt;
- font-weight: bold;
+ background-color: transparent;
+ color: @nord5;
}
-/* Each module */
-#battery,
-#clock,
-#cpu,
-#memory,
-#custom-clockify,
-#custom-spotify,
-#backlight,
-#mode,
-#network,
-#disk,
-#pulseaudio,
-#workspaces,
-#temperature {
- padding: 0 10px;
- margin: 5px 5px;
- border-radius: 10em;
- background-color: @backgroundlight;
- color: @foreground;
+#systemd-failed-units, #temperature, #load, #privacy, #mpris,
+#workspaces, #window, #cpu, #memory, #battery, #backlight,
+#wireplumber, #network, #bluetooth, #clock, #tray,
+#custom-startmenu, #custom-exit, #custom-weather, #idle_inhibitor, #custom-timer,
+#language, #keyboard-state {
+ background-color: rgba(46, 52, 64, 0.9); /* nord0 with opacity */
+ color: @nord5;
+ padding: 0 12px;
+ margin: 4px 2px;
+ border-radius: 8px;
}
-/* Each module that should blink */
-#mode,
-#memory,
-#temperature,
-#battery {
- animation-timing-function: ease-out;
- animation-iteration-count: infinite;
- animation-direction: alternate;
+/* Module grouping */
+#wireplumber {
+ margin-right: 8px; /* Separate from system-status group */
}
-
-/* Each critical module */
-#memory.critical,
-#cpu.critical,
-#temperature.critical,
-#battery.critical {
- color: @critical;
+#group-system-status > * {
+ background-color: @nord2;
+ margin: 4px 1px;
}
-
-/* Each critical that should blink */
-#mode,
-#memory.critical,
-#temperature.critical,
-#battery.critical.discharging {
- animation-name: blink-critical;
- animation-duration: 2s;
-}
-
-/* Each warning */
-#network.disconnected,
-#memory.warning,
-#cpu.warning,
-#temperature.warning,
-#battery.warning {
- color: @warning;
-}
-
-#cpu.low {
- color: @memory;
-}
-
-/* Each warning that should blink */
-#battery.warning.discharging {
- animation-name: blink-warning;
- animation-duration: 3s;
-}
-
-#battery.low.discharging {
- animation-name: blink-low;
- animation-duration: 5s;
-}
-
-/* And now modules themselves in their respective order */
-
-#mode { /* Shown current Sway mode (resize etc.) */
- color: @foreground;
- background: @mode;
-}
-
-/* Workspaces stuff */
-#workspaces {
- background: #2A2A2A;
+#network, #bluetooth {
+ background-color: @nord3;
+ margin-left: 8px; /* Separate network group */
}
+/* Module-specific colors and styles */
#workspaces button {
- padding: 0;
- color: #D4BE98;
- font-size: 16pt;
+ color: @nord7;
+ font-size: 16px;
+ padding: 0 8px;
+ border-radius: 8px;
}
-
-#workspaces button.persistent {
- color: #3E3E3E;
-}
-
#workspaces button.visible {
- color: #FE8019;
-}
-
-#network {
- color: @network;
-}
-
-#disk {
- color: #FABD2F;
- border-radius: 10em 0 0 10em;
- margin-right: 0;
-}
-
-#memory {
- color: @memory;
- margin-right: 0;
- margin-left: 0;
- border-radius: 0;
-}
-
-#cpu {
- color: @cpu;
- margin-right: 0;
- margin-left: 0;
- border-radius: 0;
-}
-
-#temperature {
- color: @temp;
- border-radius: 0 10em 10em 0;
- margin-left: 0;
-}
-
-#pulseaudio {
- color: @sound;
- margin-right: 0;
- border-radius: 10em 0 0 10em;
-}
-
-#pulseaudio.muted {
- color: #3E3E3E;
-}
-
-#battery {
- color: @battery;
- margin-right: 0;
- margin-left: 0;
- border-radius: 0;
-}
-
-#backlight {
- color: #D3869B;
- margin-left: 0;
- border-radius: 0 10em 10em 0;
-}
-
-#clock.date {
- background: @date;
- color: @foreground;
- margin-right: 0;
- border-radius: 10em 0 0 10em;
-}
-
-#clock.time {
- background: @time;
- color: @background;
- margin-left: 0;
- border-radius: 0 10em 10em 0;
+ color: @nord8;
+ background-color: @nord1;
+}
+#workspaces button:hover {
+ background-color: @nord2;
+ color: @nord6;
+ opacity: 0.9;
+}
+ #workspaces button.active {
+ color: @nord6;
+}
+#cpu { color: @nord9; }
+#memory { color: @nord7; }
+#battery { color: @nord7; }
+#mpris { color: @nord7; }
+#backlight { color: @nord9; }
+#wireplumber { color: @nord9; }
+#network { color: @nord7; min-width: 220px; } /* Fixed width for consistent alignment */
+#bluetooth { color: @nord10; }
+#language {
+ color: @nord10;
+ min-width: 40px; /* Ensure visibility */
+ background-color: rgba(46, 52, 64, 0.9); /* Explicit nord0 background */
+}
+#keyboard-state { color: @nord10; }
+#custom-weather { color: @nord7; }
+#clock { background-color: @nord1; }
+#custom-startmenu, #custom-exit, #idle_inhibitor {
+ font-size: 16px;
+}
+#custom-startmenu:hover, #custom-exit:hover, #idle_inhibitor:hover {
+ background-color: @nord2;
+ color: @nord6;
+ opacity: 0.9;
+}
+
+/* Warning and Critical States */
+#cpu.warning, #memory.warning, #battery.warning {
+ color: @nord13;
+ animation: blink-warning 2s ease-in-out infinite alternate;
+}
+#cpu.critical, #memory.critical, #battery.critical {
+ color: @nord11;
+ animation: blink-critical 1s ease-in-out infinite alternate;
+}
+#network.disconnected {
+ color: @nord11;
+}
+
+/* Animations */
+@keyframes blink-warning {
+ to { background-color: @nord13; color: @nord0; }
}
-
-#tray {
- margin: 0 5px;
+@keyframes blink-critical {
+ to { background-color: @nord11; color: @nord0; }
}
+/* Tooltip */
tooltip {
- color: @foreground;
- background-color: @background;
- font-weight: 400;
- font-family: Fira Code;
- margin: 0 10px;
- border-radius: 1em;
+ background-color: @nord0;
+ color: @nord5;
+ border-radius: 8px;
+ padding: 5px 10px;
}
-
tooltip label {
- color: @foreground;
- padding: 0 5px;
+ color: @nord5;
+}
+
+/* Tray */
+#tray > .passive {
+ color: @nord3;
}
+#tray > .active {
+ color: @nord8;
+ }
diff --git a/hosts/kataja/default.nix b/hosts/kataja/default.nix
new file mode 100644
index 0000000..f664cab
--- /dev/null
+++ b/hosts/kataja/default.nix
@@ -0,0 +1,141 @@
+# Edit this configuration file to define what should be installed on
+# your system. Help is available in the configuration.nix(5) man page
+# and in the NixOS manual (accessible by running ‘nixos-help’).
+
+{
+ inputs,
+ outputs,
+ lib,
+ config,
+ pkgs,
+ vars,
+ ...
+}:
+
+{
+ imports = [ ./hardware-configuration.nix ];
+ networking = {
+ hostName = "kataja";
+ interfaces.eno1.wakeOnLan.enable = true;
+ };
+
+ systemd = {
+ network = {
+ enable = true;
+ wait-online.enable = false;
+ config = {
+ networkConfig = {
+ SpeedMeter = true;
+ };
+ };
+ networks = {
+ "10-wlan" = {
+ matchConfig = {
+ MACAddress = "10:6f:d9:28:3f:b3";
+ };
+ linkConfig = {
+ Multicast = true;
+ };
+ networkConfig = {
+ DHCP = true;
+ IPv6AcceptRA = true;
+ MulticastDNS = true;
+ UseDomains = true;
+ };
+ dhcpV4Config = {
+ RouteMetric = 600;
+ UseMTU = true;
+ };
+ ipv6AcceptRAConfig = {
+ RouteMetric = 600;
+ UseMTU = true;
+ };
+ linkConfig.RequiredForOnline = "no";
+ };
+ "11-lan" = {
+ matchConfig = {
+ MACAddress = "70:70:fc:00:29:25";
+ };
+ linkConfig = {
+ Multicast = true;
+ };
+ networkConfig = {
+ DHCP = true;
+ IPv6AcceptRA = true;
+ MulticastDNS = true;
+ UseDomains = true;
+ };
+ dhcpV4Config = {
+ RouteMetric = 100;
+ UseMTU = true;
+ };
+ ipv6AcceptRAConfig = {
+ RouteMetric = 100;
+ UseMTU = true;
+ };
+ linkConfig.RequiredForOnline = "no";
+ };
+ "12-lan" = {
+ matchConfig = {
+ MACAddress = "70:70:fc:00:29:26";
+ };
+ networkConfig = {
+ DHCP = true;
+ IPv6AcceptRA = true;
+ UseDomains = true;
+ };
+ ipv6AcceptRAConfig = {
+ UseMTU = true;
+ };
+ linkConfig.RequiredForOnline = "no";
+ };
+ };
+ };
+ sleep.extraConfig = "HibernateDelaySec=90m";
+ };
+
+ environment = {
+ sessionVariables = {
+ LIBVA_DRIVER_NAME = "radeonsi";
+ };
+ };
+
+ services = {
+ godns = {
+ enable = true;
+ settings = {
+ domains = [
+ {
+ domain_name = "www.duckdns.org";
+ sub_domains = [ "kataja" ];
+ }
+ ];
+ ip_interface = "eno1";
+ interval = 300;
+ ip_type = "IPv6";
+ login_token_file = config.age.secrets.duckdns.path;
+ provider = "DuckDNS";
+ };
+ };
+ xserver = {
+ deviceSection = ''
+ Option "DRI" "3"
+ Option "VariableRefresh" "True"
+ Option "TearFree" "False"
+ '';
+ };
+ };
+
+ hardware = {
+ graphics = {
+ extraPackages = with pkgs; [
+ amdvlk
+ rocmPackages.clr.icd
+ ];
+ };
+
+ bluetooth.settings.General.Name = "kataja";
+ };
+
+ system.stateVersion = "24.05";
+}
diff --git a/hosts/kataja/hardware-configuration.nix b/hosts/kataja/hardware-configuration.nix
new file mode 100644
index 0000000..5d1d1b7
--- /dev/null
+++ b/hosts/kataja/hardware-configuration.nix
@@ -0,0 +1,79 @@
+# 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 = {
+ kernelPackages = pkgs.linuxPackages_zen;
+ initrd = {
+ luks.devices."luks-b3e9864c-2a40-43d7-b1a0-51f3b2ec42bc".device =
+ "/dev/disk/by-uuid/b3e9864c-2a40-43d7-b1a0-51f3b2ec42bc";
+ availableKernelModules = [
+ "ahci"
+ "amdgpu"
+ "mt7921e"
+ "nvme"
+ "r8169"
+ "sd_mod"
+ "usb_storage"
+ "usbhid"
+ "xhci_pci"
+ "cdc_acm"
+ ];
+ };
+ kernelParams = [
+ "amdgpu.modeset=1"
+ "default_hugepagesz=2M"
+ "mitigations=off"
+ "nospectre_v2"
+ "transparent_hugepage=always"
+ ];
+ kernelModules = [
+ "k10temp"
+ "kvm-amd"
+ "snd_hda_intel"
+ "tcp_bbr"
+ ];
+ extraModulePackages = [ ];
+ };
+
+ fileSystems."/" = {
+ device = "/dev/disk/by-uuid/3e7f6b6b-b656-4f42-b314-f447e2562718";
+ fsType = "ext4";
+ };
+
+ boot.initrd.luks.devices."luks-b9b738dc-8554-41d9-82f9-042663b52e79".device =
+ "/dev/disk/by-uuid/b9b738dc-8554-41d9-82f9-042663b52e79";
+
+ fileSystems."/boot" = {
+ device = "/dev/disk/by-uuid/4121-BEC3";
+ fsType = "vfat";
+ options = [
+ "fmask=0077"
+ "dmask=0077"
+ ];
+ };
+
+ swapDevices = [ { device = "/dev/disk/by-uuid/97641877-607c-46cd-853d-e431c8a90dc2"; } ];
+
+ # 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.<interface>.useDHCP`.
+ # networking.useDHCP = lib.mkDefault true;
+ # networking.interfaces.eno1.useDHCP = lib.mkDefault true;
+ # networking.interfaces.enp2s0.useDHCP = lib.mkDefault true;
+ # networking.interfaces.wlp3s0.useDHCP = lib.mkDefault true;
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/pihlaja/default.nix b/hosts/pihlaja/default.nix
new file mode 100644
index 0000000..8101815
--- /dev/null
+++ b/hosts/pihlaja/default.nix
@@ -0,0 +1,269 @@
+# Edit this configuration file to define what should be installed on
+# your system. Help is available in the configuration.nix(5) man page
+# and in the NixOS manual (accessible by running ‘nixos-help’).
+
+{
+ inputs,
+ outputs,
+ lib,
+ config,
+ pkgs,
+ vars,
+ ...
+}:
+
+{
+ imports = [ ./hardware-configuration.nix ];
+
+ hardware = {
+ bluetooth.enable = true;
+ keyboard.qmk.enable = true;
+ logitech.wireless.enable = true;
+ nvidia = {
+ modesetting.enable = true;
+ powerManagement = {
+ enable = true;
+ finegrained = false;
+ };
+ open = true;
+ nvidiaSettings = false;
+ package = config.boot.kernelPackages.nvidiaPackages.latest;
+ };
+ graphics = {
+ extraPackages = with pkgs; [
+ nvidia-vaapi-driver
+ cudatoolkit
+ ];
+ };
+
+ bluetooth.settings.General.Name = "pihlaja";
+ };
+
+ networking = {
+ hostName = "pihlaja";
+ interfaces.enp5s0.wakeOnLan.enable = true;
+ };
+
+ environment = {
+ sessionVariables = {
+ LIBVA_DRIVER_NAME = "nvidia";
+ VK_DRIVER_FILES = "/run/opengl-driver/share/vulkan/icd.d/nvidia_icd.x86_64.json";
+ GBM_BACKEND = "nvidia-drm";
+ __GLX_VENDOR_LIBRARY_NAME = "nvidia";
+ NVD_BACKEND = "direct";
+ };
+ systemPackages = with pkgs; [
+ logiops
+ qmk_hid
+ qmk
+ via
+ qmk-udev-rules
+ ];
+ };
+
+ services.hardware.openrgb = {
+ enable = true;
+ package = pkgs.openrgb-with-all-plugins;
+ motherboard = "amd";
+ server = {
+ port = 6742;
+ };
+ };
+
+ systemd = {
+ network = {
+ enable = true;
+ wait-online.enable = false;
+ config = {
+ networkConfig = {
+ SpeedMeter = true;
+ };
+ };
+ networks = {
+ "10-wlan" = {
+ matchConfig.MACAddress = "34:cf:f6:25:c7:db";
+ linkConfig = {
+ Multicast = true;
+ };
+ networkConfig = {
+ DHCP = "yes";
+ IPv6AcceptRA = true;
+ MulticastDNS = true;
+ LinkLocalAddressing = "ipv6";
+ DNSSEC = true;
+ UseDomains = true;
+ };
+ dhcpV4Config = {
+ RouteMetric = 100;
+ UseDNS = true;
+ UseMTU = true;
+ };
+ ipv6AcceptRAConfig = {
+ UseMTU = true;
+ };
+ linkConfig.RequiredForOnline = "yes";
+ };
+ "11-lan" = {
+ matchConfig.MACAddress = "18:c0:4d:04:cf:e9";
+ linkConfig = {
+ Multicast = true;
+ };
+ networkConfig = {
+ DHCP = "yes";
+ IPv6AcceptRA = true;
+ MulticastDNS = true;
+ LinkLocalAddressing = "ipv6";
+ DNSSEC = true;
+ UseDomains = true;
+ };
+ dhcpV4Config = {
+ RouteMetric = 90;
+ UseDNS = true;
+ UseMTU = true;
+ };
+ ipv6AcceptRAConfig = {
+ RouteMetric = 80;
+ UseMTU = true;
+ };
+ linkConfig.RequiredForOnline = "no";
+ };
+ };
+ };
+ services = {
+ logiops = {
+ enable = true;
+ description = "An unofficial userspace driver for HID++ Logitech devices";
+ serviceConfig = {
+ Type = "simple";
+ ExecStart = "${pkgs.logiops}/bin/logid";
+ };
+ wantedBy = [ "multi-user.target" ];
+ };
+ };
+ };
+
+ services = {
+ godns = {
+ enable = true;
+ settings = {
+ domains = [
+ {
+ domain_name = "www.duckdns.org";
+ sub_domains = [ "pihlaja" ];
+ }
+ ];
+ ip_interface = "enp5s0";
+ interval = 300;
+ ip_type = "IPv6";
+ login_token_file = config.age.secrets.duckdns.path;
+ provider = "DuckDNS";
+ };
+ };
+ xserver = {
+ videoDrivers = [ "nvidia" ];
+ };
+ udev.packages = with pkgs; [
+ qmk
+ qmk_hid
+ qmk-udev-rules
+ via
+ ];
+ llama-cpp = {
+ enable = false;
+ openFirewall = true;
+ model = "/home/petri/Programming/DeepSeek-R1-Distill-Qwen-32B-Q8_0.gguf";
+ host = "0.0.0.0";
+ };
+ };
+ fonts.fontconfig.subpixel.rgba = "bgr";
+
+ # Configuration for logiops
+ environment.etc."logid.cfg".text = ''
+ devices: ({
+ name: "Wireless Mouse MX Master 3";
+ smartshift: {
+ on: true;
+ threshold: 12;
+ };
+ hiresscroll: {
+ hires: true;
+ target: false;
+ };
+ thumbwheel: {
+ divert: true;
+ invert: false;
+ left: {
+ threshold: 1;
+ interval: 1;
+ direction: "Left";
+ mode: "OnInterval";
+ action = {
+ type: "Keypress";
+ keys: ["KEY_VOLUMEDOWN"];
+ };
+ };
+ right: {
+ threshold: 1;
+ interval: 1;
+ direction: "Right";
+ mode: "OnInterval";
+ action = {
+ type: "Keypress";
+ keys: ["KEY_VOLUMEUP"];
+ };
+ };
+ };
+ dpi: 1500;
+ buttons: ({
+ cid: 0xc3;
+ action = {
+ type: "Gestures";
+ gestures: ({
+ direction: "Left";
+ mode: "OnRelease";
+ action = {
+ type = "Keypress";
+ keys: ["KEY_F15"];
+ };
+ }, {
+ direction: "Right";
+ mode: "OnRelease";
+ action = {
+ type = "Keypress";
+ keys: ["KEY_F16"];
+ };
+ }, {
+ direction: "Down";
+ mode: "OnRelease";
+ action = {
+ type: "Keypress";
+ keys: ["KEY_F17"];
+ };
+ }, {
+ direction: "Up";
+ mode: "OnRelease";
+ action = {
+ type: "Keypress";
+ keys: ["KEY_F18"];
+ };
+ }, {
+ direction: "None";
+ mode: "OnRelease";
+ action = {
+ type = "Keypress";
+ keys: ["KEY_PLAYPAUSE"];
+ };
+ });
+ };
+ }, {
+ cid: 0xc4;
+ action = {
+ type: "Keypress";
+ keys: ["KEY_F19"];
+ };
+ });
+ });
+ '';
+
+ system.stateVersion = "24.05"; # Did you read the comment?
+}
diff --git a/hosts/pihlaja/hardware-configuration.nix b/hosts/pihlaja/hardware-configuration.nix
new file mode 100644
index 0000000..1e81932
--- /dev/null
+++ b/hosts/pihlaja/hardware-configuration.nix
@@ -0,0 +1,80 @@
+# 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 = {
+ kernelPackages = pkgs.linuxPackages;
+ kernelParams = [
+ "NVreg_PreserveVideoMemoryAllocations=1"
+ "mitigations=off"
+ "nospectre_v2"
+ "nvidia_drm.fbdev=1"
+ "nvidia_drm.modeset=1"
+ "transparent_hugepage=always"
+ ];
+ initrd = {
+ luks.devices."luks-a47a6087-e6bf-4e8d-a034-2229259c38f5".device =
+ "/dev/disk/by-uuid/a47a6087-e6bf-4e8d-a034-2229259c38f5";
+ availableKernelModules = [
+ "cdc_acm"
+ "ahci"
+ "iwldvm"
+ "iwlwifi"
+ "nvme"
+ "sd_mod"
+ "usb_storage"
+ "usbhid"
+ "xhci_pci"
+ "nvidia"
+ "nvidia_drm"
+ "nvidia_modeset"
+ "nvidia_uvm"
+ ];
+ luks.devices."luks-1efa1182-2964-49bf-a413-fb51bd7b107f".device =
+ "/dev/disk/by-uuid/1efa1182-2964-49bf-a413-fb51bd7b107f";
+ };
+ kernelModules = [
+ "tcp_bbr"
+ "iwldvm"
+ "iwlwifi"
+ "k10temp"
+ "kvm-amd"
+ ];
+ extraModulePackages = [ ];
+ };
+
+ fileSystems."/" = {
+ device = "/dev/disk/by-uuid/eecfd181-138c-4c90-add6-2ef528d5dbb4";
+ fsType = "ext4";
+ };
+
+ fileSystems."/data" = {
+ device = "/dev/disk/by-uuid/a02a4e78-32ea-40e6-8398-087bfd8dca93";
+ fsType = "ext4";
+ options = [ "nofail" ];
+ };
+
+ fileSystems."/boot" = {
+ device = "/dev/disk/by-uuid/A4F5-0899";
+ fsType = "vfat";
+ options = [
+ "fmask=0077"
+ "dmask=0077"
+ ];
+ };
+
+ swapDevices = [ { device = "/dev/disk/by-uuid/d0e5a93d-18a7-4d24-893c-d2417c3d5043"; } ];
+
+ nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+ hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/hosts/saarni/default.nix b/hosts/saarni/default.nix
index 010a8eb..5252e6f 100644
--- a/hosts/saarni/default.nix
+++ b/hosts/saarni/default.nix
@@ -2,249 +2,147 @@
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).
-{ inputs, outputs, lib, config, pkgs, vars, ... }:
+{
+ inputs,
+ outputs,
+ lib,
+ config,
+ pkgs,
+ vars,
+ ...
+}:
{
- imports = [
- ./hardware-configuration.nix
- inputs.home-manager.nixosModules.home-manager
- ];
+ imports = [ ./hardware-configuration.nix ];
- nix = {
- nixPath = [ "/etc/nix/path" ];
- gc = {
- automatic = true;
- };
- settings = {
- auto-optimise-store = true;
- experimental-features = "nix-command flakes";
+ hardware = {
+ graphics = {
+ extraPackages = with pkgs; [
+ libvpl
+ libvdpau-va-gl
+ sof-firmware
+ vpl-gpu-rt
+ ];
};
- registry = (lib.mapAttrs (_: flake: { inherit flake; }))
- ((lib.filterAttrs (_: lib.isType "flake")) inputs);
- };
- nixpkgs.config.allowUnfree = true;
- security.rtkit.enable = true;
-
- home-manager = {
- extraSpecialArgs = { inherit inputs outputs vars; };
- users = { ${vars.user} = import ../../home; };
- };
-
- powerManagement = {
- enable = true;
- powertop.enable = true;
- };
- # Bootloader.
- boot = {
- loader = {
- systemd-boot.enable = true;
- efi.canTouchEfiVariables = true;
- };
- kernelPackages = pkgs.linuxPackages_latest;
- plymouth.enable = true;
- kernelParams = [ "quiet" "splash" ];
+ bluetooth.settings.General.Name = "saarni";
};
networking = {
- nameservers = [ "1.1.1.1#one.one.one.one" "1.0.0.1#one.one.one.one" ];
hostName = "saarni";
- useNetworkd = true;
- wireless.iwd.enable = true;
};
systemd = {
- watchdog = {
- device = "/dev/watchdog";
- runtimeTime = "30s";
+ services."enable-wifi-on-boot" = {
+ description = "Enable wifi on boot";
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ ExecStart = "${pkgs.util-linux}/bin/rfkill unblock all";
+ Type = "oneshot";
+ };
};
- sleep.extraConfig = "HibernateDelaysSec=90m";
network = {
enable = true;
wait-online.enable = false;
+ config = {
+ networkConfig = {
+ SpeedMeter = true;
+ };
+ };
networks = {
"10-lan" = {
- matchConfig.Name = "enp0s20f0u9";
- networkConfig.DHCP = "yes";
+ matchConfig = {
+ Name = "enps0";
+ };
+ linkConfig = {
+ Multicast = true;
+ };
+ networkConfig = {
+ DHCP = true;
+ IPv6AcceptRA = true;
+ MulticastDNS = true;
+ LinkLocalAddressing = "ipv6";
+ DNSSEC = true;
+ UseDomains = true;
+ };
+ dhcpV4Config = {
+ RouteMetric = 20;
+ UseDNS = true;
+ UseMTU = true;
+ };
+ ipv6AcceptRAConfig = {
+ UseMTU = true;
+ };
};
- "11-lan" = {
- matchConfig.Name = "wlan0";
- networkConfig.DHCP = "yes";
+ "11-wlan" = {
+ matchConfig = {
+ MACAddress = "64:d6:9a:9e:06:60";
+ };
+ linkConfig = {
+ Multicast = true;
+ };
+ networkConfig = {
+ DHCP = true;
+ IPv6AcceptRA = true;
+ MulticastDNS = true;
+ LinkLocalAddressing = "ipv6";
+ DNSSEC = true;
+ UseDomains = true;
+ };
+ dhcpV4Config = {
+ RouteMetric = 100;
+ UseDNS = true;
+ UseMTU = true;
+ SendHostname = true;
+ };
+ dhcpV6Config = {
+ WithoutRA = "solicit";
+ };
+ ipv6AcceptRAConfig = {
+ UseMTU = true;
+ };
};
};
};
};
- system.autoUpgrade = {
- enable = true;
- };
-
- sound.enable = true;
-
- hardware = {
- opengl = {
- enable = true;
- driSupport = true;
- extraPackages = with pkgs; [
- intel-media-driver
- libvdpau-va-gl
- ];
- };
- bluetooth = {
- enable = true;
- powerOnBoot = true;
- };
- };
-
- # Set your time zone.
- time.timeZone = "Europe/Helsinki";
-
- # Select internationalisation properties.
- i18n = {
- defaultLocale = "en_US.UTF-8";
- extraLocaleSettings = {
- LC_ADDRESS = "fi_FI.UTF-8";
- LC_IDENTIFICATION = "fi_FI.UTF-8";
- LC_MEASUREMENT = "fi_FI.UTF-8";
- LC_MONETARY = "fi_FI.UTF-8";
- LC_NAME = "fi_FI.UTF-8";
- LC_NUMERIC = "fi_FI.UTF-8";
- LC_PAPER = "fi_FI.UTF-8";
- LC_TELEPHONE = "fi_FI.UTF-8";
- LC_TIME = "fi_FI.UTF-8";
- };
- };
-
- console.keyMap = "fi";
-
- # Define a user account. Don't forget to set a password with ‘passwd’.
- users.users.${vars.user} = {
- isNormalUser = true;
- description = "${vars.name}";
- extraGroups = [ "input" "network" "wheel" ];
- packages = with pkgs; [ ];
- ignoreShellProgramCheck = true;
- shell = pkgs.${vars.shell};
- };
-
- programs = {
- firefox.enable = true;
- hyprland.enable = true;
- gnupg.agent = {
- enable = true;
- enableSSHSupport = true;
- };
- mtr.enable = true;
- };
-
environment = {
- sessionVariables = {
- LIBVA_DRIVER_NAME = "iHD";
- };
- systemPackages = with pkgs; [
- fastfetch
- vulkan-tools
- libva-utils
- wget
- curl
- git
- gzip
- btop
- glxinfo
- sqlite
- openssh
- gnupg
- helix
- nixfmt
- ];
- etc = lib.mapAttrs' (name: value: {
- name = "nix/path/${name}";
- value.source = value.flake;
- }) config.nix.registry;
- };
-
- fonts = {
- enableDefaultPackages = true;
- packages = with pkgs; [ noto-fonts fira-code fira-code-symbols ];
- fontconfig = {
- defaultFonts = {
- serif = [ "Noto Serif" ];
- sansSerif = [ "Noto Sans" ];
- monospace = [ "Fira Code" ];
- };
- };
+ sessionVariables = { };
};
- # List services that you want to enable:
services = {
- getty.autologinUser = "petri";
- resolved = {
- enable = true;
- dnsovertls = "true";
- };
- xserver = {
- layout = "fi";
- xkbVariant = "normal";
- };
- pipewire = {
+ godns = {
enable = true;
- alsa.enable = true;
- pulse.enable = true;
- };
- openssh.enable = true;
- printing.enable = true;
- kmscon = {
- enable = true;
- hwRender = true;
- extraOptions = "--xkb-layout=fi";
- };
- blueman.enable = true;
- upower.enable = true;
- upower.criticalPowerAction = "Hibernate";
- fstrim.enable = false;
- thermald.enable = true;
- auto-cpufreq.enable = true;
- greetd = {
- enable = true;
- settings = rec {
- initial_session = {
- command = "${pkgs.hyprland}/bin/Hyprland";
- user = "petri";
- };
- default_session = initial_session;
+ settings = {
+ domains = [
+ {
+ domain_name = "www.duckdns.org";
+ sub_domains = [ "saarni" ];
+ }
+ ];
+ ip_interface = "wlan0";
+ interval = 300;
+ ip_type = "IPv6";
+ login_token_file = config.age.secrets.duckdns.path;
+ provider = "DuckDNS";
};
};
- };
-
- xdg.portal = {
- enable = true;
- xdgOpenUsePortal = true;
- extraPortals = [
- pkgs.xdg-desktop-portal-hyprland
- pkgs.xdg-desktop-portal-gtk
- ];
- wlr = {
+ tlp = {
enable = true;
settings = {
- screencast = {
- chooser_type = "simple";
- chooser_cmd = "${pkgs.slurp}/bin/slurp -f %o -ro";
- };
+ CPU_PERF_POLICY_ON_AC = "performance";
+ CPU_PERF_POLICY_ON_BAT = "power";
+ CPU_SCALING_GOVERNOR_ON_AC = "performance";
+ CPU_SCALING_GOVERNOR_ON_BAT = "powersave";
+ START_CHARGE_THRESH_BAT0 = 40;
+ STOP_CHARGE_TRESH_BAT0 = 20;
};
};
+ upower = {
+ enable = true;
+ criticalPowerAction = "Hibernate";
+ };
+ thermald.enable = true;
};
-
- # Open ports in the firewall.
- # networking.firewall.allowedTCPPorts = [ ... ];
- # networking.firewall.allowedUDPPorts = [ ... ];
- # Or disable the firewall altogether.
- # networking.firewall.enable = false;
-
- # This value determines the NixOS release from which the default
- # settings for stateful data, like file locations and database versions
- # on your system were taken. It‘s perfectly fine and recommended to leave
- # this value at the release version of the first install of this system.
- # Before changing this value read the documentation for this option
- # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
- system.stateVersion = "23.11"; # Did you read the comment?
+ system.stateVersion = "24.05";
}
diff --git a/hosts/saarni/hardware-configuration.nix b/hosts/saarni/hardware-configuration.nix
index 6aef572..798aa7b 100644
--- a/hosts/saarni/hardware-configuration.nix
+++ b/hosts/saarni/hardware-configuration.nix
@@ -1,37 +1,72 @@
# 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") ];
+ config,
+ lib,
+ pkgs,
+ modulesPath,
+ ...
+}:
- boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" ];
- boot.initrd.kernelModules = [ ];
- boot.kernelModules = [ "kvm-intel" ];
- boot.extraModulePackages = [ ];
+{
+ imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
- fileSystems."/" =
- { device = "/dev/disk/by-uuid/3968a07a-5e37-4bdb-9e3a-6c74950fd03d";
- fsType = "ext4";
+ boot = {
+ kernelPackages = pkgs.linuxPackages_zen;
+ initrd = {
+ availableKernelModules = [
+ "xhci_pci"
+ "thunderbolt"
+ "nvme"
+ "usb_storage"
+ "sd_mod"
+ "i915"
+ "cdc_acm"
+ ];
};
+ kernelModules = [
+ "kvm-intel"
+ "iwlwifi"
+ "iwldvm"
+ "snd_hda_intel"
+ "snd_soc_avs"
+ "snd_sof_pci_intel_tgl"
+ "tcp_bbr"
+ ];
+ extraModulePackages = [ ];
+ kernelParams = [
+ "default_hugepagesz=2M"
+ "i915.enable_guc=3"
+ "i915.fastboot=1"
+ "mitigations=off"
+ "nospectre_v2"
+ "snd-intel-dspcfg.dsp_driver=3"
+ "transparent_hugepage=always"
+ "usbcore.blinkenlights=1"
+ ];
+ extraModprobeConfig = ''
+ options snd_hda_intel enable=0,1
+ '';
+ };
- fileSystems."/boot" =
- { device = "/dev/disk/by-uuid/9B7E-3D1B";
- fsType = "vfat";
- options = [ "fmask=0022" "dmask=0022" ];
- };
+ fileSystems."/" = {
+ device = "/dev/disk/by-uuid/3968a07a-5e37-4bdb-9e3a-6c74950fd03d";
+ fsType = "ext4";
+ };
- swapDevices = [ ];
+ fileSystems."/boot" = {
+ device = "/dev/disk/by-uuid/9B7E-3D1B";
+ fsType = "vfat";
+ options = [
+ "fmask=0022"
+ "dmask=0022"
+ ];
+ };
- # 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.<interface>.useDHCP`.
- # networking.useDHCP = lib.mkDefault true;
- # networking.interfaces.wlan0.useDHCP = lib.mkDefault true;
+ hardware.firmware = [ pkgs.sof-firmware ];
+ swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
diff --git a/hosts/tammi/20-wired.network b/hosts/tammi/20-wired.network
new file mode 100644
index 0000000..1a39f55
--- /dev/null
+++ b/hosts/tammi/20-wired.network
@@ -0,0 +1,31 @@
+# WAN
+[Match]
+Name=eth0
+
+[Network]
+DHCP=yes
+IPv6PrivacyExtensions=no
+IPv6AcceptRA=yes
+IPMasquerade=both
+DHCPPrefixDelegation=yes
+IPForward=yes
+
+[DHCPv4]
+UseMTU=yes
+UseDNS=false
+UseNTP=false
+
+[IPv6AcceptRA]
+UseMTU=yes
+UseDNS=false
+
+[DHCPv6]
+WithoutRA=solicit
+PrefixDelegationHint=::/56
+RapidCommit=yes
+
+[DHCPPrefixDelegation]
+UplinkInterface=auto
+SubnetId=auto
+Announce=yes
+Assign=yes
diff --git a/hosts/tammi/21-wired.network b/hosts/tammi/21-wired.network
new file mode 100644
index 0000000..4ab692d
--- /dev/null
+++ b/hosts/tammi/21-wired.network
@@ -0,0 +1,48 @@
+[Match]
+# Name=eth1 - LAN
+MACAddress=6c:1f:f7:1b:f2:6c
+
+[Link]
+Multicast=yes
+
+[Network]
+DNS=10.1.1.1#adguard.tammi.cc
+DNS=2001:14ba:a304:aff4::1#adguard.tammi.cc
+NTP=10.1.1.1
+DHCP=no
+LLDP=yes
+LLMNR=yes
+Address=10.1.1.1/24
+DHCPServer=true
+MulticastDNS=true
+IPv6SendRA=yes
+IPv6AcceptRA=no
+IPv6PrefixDelegation=yes
+DHCPPrefixDelegation=yes
+LinkLocalAddressing=yes
+LLMNR=yes
+IPForward=yes
+
+[DHCPServer]
+PoolOffset=100
+PoolSize=40
+EmitDNS=yes
+EmitNTP=yes
+EmitTimezone=yes
+DNS=_server_address
+NTP=_server_address
+DefaultLeaseTimeSec=3600
+MaxLeaseTimeSec=7200
+
+[IPv6SendRA]
+Managed=yes
+OtherInformation=yes
+EmitDNS=yes
+DNS=_link_local
+RouterPreference=high
+
+[DHCPPrefixDelegation]
+UplinkInterface=eth0
+SubnetId=0
+Announce=yes
+Assign=yes
diff --git a/hosts/tammi/CM3588.md b/hosts/tammi/CM3588.md
new file mode 100644
index 0000000..d62780a
--- /dev/null
+++ b/hosts/tammi/CM3588.md
@@ -0,0 +1,284 @@
+---
+title: "Running the gateway for testing"
+author: [Petri Hienonen]
+date: "2023-03-31"
+---
+
+# CM3588 setup guide for Debian Bookworm
+
+[CM3588](https://www.friendlyelec.com/index.php?route=product/product&product_id=294) works as our reference hardware.
+
+`SDCARD` to `EMMC` image [should be used](https://drive.google.com/file/d/1CrYDAZFwGdZoFIRfrQGEVd6SEu6f0PwU/view)
+
+[Wiki documents things related to the device installation](https://wiki.friendlyelec.com/wiki/index.php/NanoPi_R5S)
+
+## Basic configuration
+
+Login with SSH (Username: `pi`, Password: `pi`)
+
+`/etc/systemd/network/20-wired.network`
+
+```
+[Match]
+Name=eth0
+
+[Network]
+DHCP=yes
+DNS=8.8.8.8
+MulticastDNS=true
+
+[Link]
+MTUBytes=9000
+```
+
+`/etc/systemd/timesyncd.conf`
+
+```
+[Time]
+NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org
+FallbackNTP=0.pool.ntp.org 1.pool.ntp.org 0.fr.pool.ntp.org
+```
+
+```bash
+sudo apt update && sudo apt dist-upgrade -y && sudo apt autoremove -y
+sudo systemctl stop NetworkManager
+sudo systemctl disable NetworkManager
+sudo apt remove network-manager ntp wpa_supplicant
+sudo systemctl enable systemd-timesyncd.service
+sudo systemctl start systemd-timesyncd.service
+sudo timedatectl set-timezone Europe/Helsinki
+```
+
+Modify `/etc/hostname` to wanted (tammi.cc).
+
+Modify `/etc/systemd/journald.conf` (following keys):
+
+```
+[Journal]
+Storage=volatile
+SystemMaxUse=20M
+RuntimeMaxUse=20M
+MaxRetentionSec=2day
+```
+
+Wireless network configuration (`wlan0` with [`iwd`](https://iwd.wiki.kernel.org/))
+
+```bash
+sudo apt remove wpasupplicant
+sudo apt install iwd
+sudo mkdir -p /var/lib/iwd/
+```
+
+`/etc/systemd/network/20-wired.network`
+
+```
+[Match]
+Name=eth0
+
+[Network]
+DHCP=yes
+DNSSEC=allow-downgrade
+DNS=9.9.9.9 2620:fe::fe
+LinkLocalAddresssing=yes
+```
+
+`/etc/systemd/network/26-wireless.network`
+
+```
+[Match]
+Name=wlan0
+
+[Network]
+DHCP=yes
+DNSSEC=allow-downgrade
+DNS=9.9.9.9 2620:fe::fe
+LinkLocalAddresssing=yes
+```
+
+`/var/lib/iwd/example_network.psk`:
+
+```
+[Security]
+Passphrase=Relynx8WP
+```
+
+```bash
+sudo systemctl start iwd.service
+sudo systemctl enable iwd.service
+sudo systemctl restart systemd-networkd.service
+```
+
+Create petri user:
+
+```bash
+sudo useradd -m petri
+sudo passwd petri (password)
+sudo usermod -a -G sudo petri
+sudo usermod -a -G systemd-journal petri
+sudo chsh -s /bin/bash petri
+sudo reboot
+```
+
+Stop autologin for user `petri` by commenting out user `petri` in `/etc/lightdm/lightdm.conf`.
+
+Login with `petri` user.
+
+Check that network looks sane:
+
+```bash
+networkctl status --all
+```
+
+Delete `pi` user:
+
+```bash
+sudo userdel -r pi
+```
+
+Create necessary keys and clone and build rust packages:
+
+```bash
+sudo apt install llvm clang libssl-dev -y
+curl --proto '=https' --tlsv1.3 -sSf https://sh.rustup.rs | sh
+source "$HOME/.cargo/env"
+ssh-keygen -t ed25519
+cat .ssh/id_ed25519.pub
+```
+
+## Applications
+
+### SSH
+
+Guideline from: https://infosec.mozilla.org/guidelines/openssh
+
+```bash
+sudo apt install mosh
+```
+
+### Backports
+
+```bash
+echo "deb http://deb.debian.org/debian bookworm-backports main contrib non-free-firmware">/etc/apt/sources.list.d/debian-12-backports.list
+```
+
+### BTRFS
+
+```bash
+sudo apt install btrfs-progs
+sudo mkfs.btrfs -m raid1 -d raid1 /dev/nvme1n1 /dev/nvme0n1
+sudo mkdir /media/data
+echo "UUID=f566eaa0-f004-4acc-9d0d-f6fb97daca5e /media/data btrfs defaults,discard=async,compress=zstd 0 0">>/etc/fstab
+
+sudo mkdir /media/data/db
+sudo chattr -R +C /media/data/db # make the DB not COW
+
+sudo mkdir /media/data/logs
+sudo chattr -R +C /media/data/logs # make the DB not COW
+```
+
+/etc/systemd/system/btrfs-scrub@.service
+
+```systemd
+[Unit]
+Description=Btrfs scrub on %f
+ConditionPathIsMountPoint=%f
+RequiresMountsFor=%f
+
+[Service]
+Nice=19
+IOSchedulingClass=idle
+KillSignal=SIGINT
+ExecStart=/usr/bin/btrfs scrub start -B %f
+```
+
+/etc/systemd/system/btrfs-scrub@.timer
+
+```systemd
+[Unit]
+Description=Btrfs scrub on %f twice per month
+
+[Timer]
+OnCalendar=*-*-1,15
+AccuracySec=1d
+RandomizedDelaySec=1w
+Persistent=true
+
+[Install]
+WantedBy=timers.target
+```
+
+```bash
+sudo systemctl start btrfs-scrub@(systemd-escape -p /media/data).service
+sudo systemctl enable --now btrfs-scrub@(systemd-escape -p /media/data).timer
+```
+
+### Docker
+
+```bash
+sudo apt install docker.io docker-compose
+sudo usermod -aG docker $USER
+sudo systemctl enable docker.service
+```
+
+### Postgresql (edit the datapaths)
+
+```bash
+sudo apt install postgresql postgresql-contrib
+sudo mkdir /media/data/db/postgresql/16/main
+sudo vim /etc/postgresql/16/main/postgresql.conf
+sudo chown -R postgres:postgres postgresql/
+```
+Add Environment=PGDATA=/media/data/db/postgresql/%I/main to /lib/systemd/system/postgresql@.service under [Service]
+
+### Miniflux
+
+```bash
+echo "deb [trusted=yes] https://repo.miniflux.app/apt/ * *" | sudo tee /etc/apt/sources.list.d/miniflux.list > /dev/null
+apt update && apt install miniflux
+systemctl status miniflux.service
+
+sudo -u postgres psql
+CREATE USER miniflix with PASSWORD 'miniflux';
+CREATE DATABASE miniflux2 OWNER miniflux;
+```
+
+### Vaultwarden (/etc/vaultwarden):
+
+```bash
+wget -O /etc/apt/trusted.gpg.d/bananian-keyring.gpg https://bitwarden-deb.tech-network.de/bananian-keyring.gpg
+echo "deb http://bitwarden-deb.tech-network.de bookworm main" > /etc/apt/sources.list.d/vaultwarden.list
+apt update && apt install vaultwarden
+
+sudo -u postgres psql
+CREATE USER vaultwarden WITH ENCRYPTED PASSWORD 'yourpassword';
+CREATE DATABASE vaultwarden OWNER vaultwarden;
+
+(vaultwarden binary will have to be compiled with hand to enable postgresql, this is irritating)
+sudo apt install libpq-dev
+(add following systemd service environment variable)
+Environment="DATABASE_URL=postgresql://vaultwarden:yourpassword@127.0.0.1:5432/vaultwarden"
+systemctl enable vaultwarden.service
+```
+
+/etc/enviroment
+
+```bash
+PATH="/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin"
+```
+
+### Webmin
+
+```bash
+curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh
+sh setup-repos.sh
+apt install webmin
+
+(configure using /etc/webmin/miniserv.conf)
+```
+
+### Minio
+
+```bash
+wget https://dl.min.io/server/minio/release/linux-arm64/minio_20240611031330.0.0_arm64.deb -O minio.deb
+sudo dpkg -i minio.deb
+```
diff --git a/hosts/tammi/Caddyfile b/hosts/tammi/Caddyfile
new file mode 100644
index 0000000..fe9d437
--- /dev/null
+++ b/hosts/tammi/Caddyfile
@@ -0,0 +1,319 @@
+# The Caddyfile is an easy way to configure your Caddy web server.
+#
+# Unless the file starts with a global options block, the first
+# uncommented line is always the address of your site.
+#
+# To use your own domain name (with automatic HTTPS), first make
+# sure your domain's A/AAAA DNS records are properly pointed to
+# this machine's public IP, then replace ":80" below with your
+# domain name.
+(cors) {
+ @cors_preflight method OPTIONS
+
+ header {
+ Access-Control-Allow-Origin "{header.origin}"
+ Vary Origin
+ Access-Control-Expose-Headers "Authorization"
+ Access-Control-Allow-Credentials "true"
+ }
+
+ handle @cors_preflight {
+ header {
+ Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE"
+ Access-Control-Max-Age "3600"
+ }
+ respond "" 204
+ }
+}
+
+{
+ metrics
+ log {
+ output file /var/log/caddy/caddy_main.log {
+ roll_size 20MiB
+ roll_keep 5
+ roll_keep_for 100d
+ }
+ format json
+ level INFO
+ }
+}
+
+tammi.cc {
+ root * /media/data/hienotammi
+ file_server browse
+ encode zstd gzip
+ log {
+ output file /media/data/logs/tammi.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+holvi.tammi.cc {
+ root * /media/data/holvi
+ file_server browse
+ encode zstd gzip
+ log {
+ output file /media/data/logs/holvi.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+grafana.tammi.cc {
+ reverse_proxy 127.0.0.1:3010
+ encode zstd gzip
+ log {
+ output file /media/data/logs/grafana.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+ntfy.tammi.cc {
+ reverse_proxy 127.0.0.1:3070
+ encode zstd gzip
+ log {
+ output file /media/data/logs/ntfy.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+adguard.tammi.cc {
+ basic_auth {
+ petri $2a$14$1gj396cBvvMoC7kW0qSZBOF9Qzwt8ewyujua1EpdDJm9gXdBFsfEe
+ }
+ reverse_proxy 127.0.0.1:3000
+ encode zstd gzip
+ log {
+ output file /media/data/logs/adguard.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+n8n.tammi.cc {
+ reverse_proxy 127.0.0.1:5678
+ encode zstd gzip
+ log {
+ output file /media/data/logs/n8n.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+couch.tammi.cc {
+ reverse_proxy 127.0.0.1:5984
+ encode zstd gzip
+ log {
+ output file /media/data/logs/couch.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+shiori.tammi.cc {
+ reverse_proxy 127.0.0.1:7766
+ encode zstd gzip
+ log {
+ output file /media/data/logs/shiori.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+read.tammi.cc {
+ reverse_proxy 127.0.0.1:7767
+ encode zstd gzip
+ log {
+ output file /media/data/logs/read.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+atuin.tammi.cc {
+ reverse_proxy 127.0.0.1:4444
+ encode zstd gzip
+ log {
+ output file /media/data/logs/atuin.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+relationship.tammi.cc {
+ root * /media/data/relationship
+ file_server
+ encode zstd gzip
+ log {
+ output file /media/data/logs/relationship.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+start.tammi.cc {
+ basic_auth {
+ petri $2a$14$1gj396cBvvMoC7kW0qSZBOF9Qzwt8ewyujua1EpdDJm9gXdBFsfEe
+ }
+ reverse_proxy 127.0.0.1:5555
+ encode zstd gzip
+ log {
+ output file /media/data/logs/start.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+bin.tammi.cc {
+ reverse_proxy 127.0.0.1:8820
+ encode zstd gzip
+ import cors {header.origin}
+ log {
+ output file /media/data/logs/bin.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+llm.tammi.cc {
+ reverse_proxy 127.0.0.1:12500
+ encode zstd gzip
+ log {
+ output file /media/data/logs/llm.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+base.tammi.cc {
+ reverse_proxy 127.0.0.1:980
+ encode zstd gzip
+ log {
+ output file /media/data/logs/base.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+threat.tammi.cc {
+ reverse_proxy 127.0.0.1:3050
+ encode zstd gzip
+ log {
+ output file /media/data/logs/threat.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+flux.tammi.cc {
+ reverse_proxy 127.0.0.1:8080
+ encode zstd gzip
+ log {
+ output file /media/data/logs/flux.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+haku.tammi.cc {
+ reverse_proxy 127.0.0.1:8888
+ encode zstd gzip
+ log {
+ output file /media/data/logs/haku.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+memos.tammi.cc {
+ reverse_proxy 127.0.0.1:8081
+ encode zstd gzip
+ log {
+ output file /media/data/logs/memos.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+dav.tammi.cc {
+ reverse_proxy 127.0.0.1:5232
+ encode zstd gzip
+ log {
+ output file /media/data/logs/haku.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+admin.tammi.cc {
+ reverse_proxy 127.0.0.1:10000
+ encode zstd gzip
+ log {
+ output file /media/data/logs/webmin.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+minio.tammi.cc {
+ reverse_proxy localhost:9001
+}
+
+s3.tammi.cc {
+ reverse_proxy localhost:9199
+}
+
+vault.tammi.cc {
+ encode zstd gzip
+ reverse_proxy /notifications/hub/negotiate 127.0.0.1:8000
+ reverse_proxy /notifications/hub 127.0.0.1:3012
+ reverse_proxy 127.0.0.1:8000 {
+ header_up X-Real-IP {remote_host}
+ }
+ log {
+ output file /media/data/logs/vault.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+audio.tammi.cc {
+ reverse_proxy 127.0.0.1:3333
+ encode zstd gzip
+ log {
+ output file /media/data/logs/audio.log {
+ roll_size 1mb
+ roll_keep 1
+ }
+ }
+}
+
+import conf.d/*
+
+# Refer to the Caddy docs for more information:
diff --git a/hosts/tammi/cgitrc b/hosts/tammi/cgitrc
new file mode 100644
index 0000000..caa011d
--- /dev/null
+++ b/hosts/tammi/cgitrc
@@ -0,0 +1,47 @@
+#
+# cgit config
+#
+
+# The defaults
+#css=/cgit.css
+#logo=/cgit.png
+cache-size=0
+branch-sort=age
+commit-sort=date
+max-stats=quarter
+root-title=tammi.cc git repositories
+robots=noindex, nofollow
+snapshots=tar.zst
+clone-url=ssh://git@tammi.cc:/git/$CGIT_REPO_URL.git
+virtual-root=/
+
+# options
+enable-index-owner=true
+enable-index-links=true
+enable-commit-graph=1
+enable-log-filecount=1
+enable-log-linecount=1
+enable-html-serving=1
+enable-http-clone=1
+enable-blame=1
+
+# mimes
+mimetype.gif=image/gif
+mimetype.html=text/html
+mimetype.jpg=image/jpeg
+mimetype.webp=image/webp
+mimetype.jpeg=image/jpeg
+mimetype.pdf=application/pdf
+mimetype.png=image/png
+mimetype.svg=image/svg+xml
+
+# plugins
+head-include=/usr/local/lib/cgit/head_include.html
+source-filter=/usr/local/lib/cgit/filters/syntax-highlighting.js
+about-filter=/usr/local/lib/cgit/filters/about-formatting-custom.sh
+auth-filter=lua:/usr/local/lib/cgit/filters/simple-authentication-modified.lua
+
+# repositories
+readme=:README.adoc
+readme=:README.md
+remove-suffix=1
diff --git a/hosts/tammi/feeds.opml b/hosts/tammi/feeds.opml
new file mode 100644
index 0000000..b0ea84f
--- /dev/null
+++ b/hosts/tammi/feeds.opml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<opml version="2.0">
+ <head>
+ <title>Miniflux</title>
+ <dateCreated>Wed, 29 Oct 2025 10:42:41 EET</dateCreated>
+ </head>
+ <body>
+ <outline text="All">
+ <outline title="Feeds" text="Feeds" xmlUrl="https://www.understandingwar.org/feeds.xml" htmlUrl="https://www.understandingwar.org/feeds2" type="rss"></outline>
+ <outline title="Starsector" text="Starsector" xmlUrl="https://fractalsoftworks.com/feed/" htmlUrl="https://fractalsoftworks.com" type="rss"></outline>
+ <outline title="Lua: news" text="Lua: news" xmlUrl="https://lua.org/news.rss" htmlUrl="https://www.lua.org/news.html" description="What&#39;s happening in the Lua world" type="rss"></outline>
+ <outline title="Command Line" text="Command Line" xmlUrl="https://www.reddit.com/r/commandline.rss" htmlUrl="https://www.reddit.com/r/commandline" description="This is for anything regarding the command line, in any operating system. All questions (including dumb ones), tips, and links to interesting programs/console applications you&#39;ve found or made yourself are welcome. Linux / BSD / OSX / Windows CLI and TUI apps or questions or comments, we&#39;re happy to take them all!" type="rss"></outline>
+ <outline title="cs.LG updates on arXiv.org" text="cs.LG updates on arXiv.org" xmlUrl="http://arxiv.org/rss/cs.LG" htmlUrl="http://arxiv.org/" type="rss"></outline>
+ <outline title="Joshua Weissman" text="Joshua Weissman" xmlUrl="https://www.youtube.com/feeds/videos.xml?channel_id=UChBEbMKI1eCcejTtmI32UEw" htmlUrl="https://www.youtube.com/channel/UChBEbMKI1eCcejTtmI32UEw" type="rss"></outline>
+ <outline title="Machine Learning" text="Machine Learning" xmlUrl="https://www.reddit.com/r/MachineLearning.rss" htmlUrl="https://www.reddit.com/r/MachineLearning" type="rss"></outline>
+ <outline title="Nautilus" text="Nautilus" xmlUrl="https://nautil.us/feed/" htmlUrl="https://nautil.us/" type="rss"></outline>
+ <outline title="network, branch master" text="network, branch master" xmlUrl="https://git.tammi.cc/network/atom" htmlUrl="https://git.tammi.cc/network/" description="Network configration to learn to configure networks" type="rss"></outline>
+ <outline title="NixOS Announcements" text="NixOS Announcements" xmlUrl="https://nixos.org/blog/announcements-rss.xml" htmlUrl="https://nixos.org/blog/" type="rss"></outline>
+ <outline title="NixOS Newsletters" text="NixOS Newsletters" xmlUrl="https://nixos.org/blog/newsletters-rss.xml" htmlUrl="https://nixos.org/blog/" type="rss"></outline>
+ <outline title="NVIDIA Blog" text="NVIDIA Blog" xmlUrl="http://feeds.feedburner.com/nvidiablog" htmlUrl="https://blogs.nvidia.com" type="rss"></outline>
+ <outline title="Relesoft Activity Feed" text="Relesoft Activity Feed" xmlUrl="https://events.tammi.cc/relesoft/activity.atom" htmlUrl="https://events.tammi.cc/relesoft/activity.atom" description="Activity feed for Relesoft organization" type="rss"></outline>
+ <outline title="Self-Hosted Alternatives to Popular Services" text="Self-Hosted Alternatives to Popular Services" xmlUrl="https://www.reddit.com/r/selfhosted.rss" htmlUrl="https://www.reddit.com/r/selfhosted" type="rss"></outline>
+ <outline title="Very Bad Wizards" text="Very Bad Wizards" xmlUrl="https://feeds.libsyn.com/474285/rss" htmlUrl="http://sites.libsyn.com/474285/site" type="rss"></outline>
+ <outline title="Writing an OS in Rust" text="Writing an OS in Rust" xmlUrl="https://os.phil-opp.com/rss.xml" htmlUrl="https://os.phil-opp.com" type="rss"></outline>
+ </outline>
+ <outline text="Economics">
+ <outline title="Ajatuspaja Libera" text="Ajatuspaja Libera" xmlUrl="https://www.libera.fi/feed" htmlUrl="https://www.libera.fi" type="rss"></outline>
+ <outline title="ECB - European Central Bank" text="ECB - European Central Bank" xmlUrl="https://www.ecb.europa.eu/rss/press.html" htmlUrl="https://www.ecb.europa.eu/" type="rss"></outline>
+ <outline title="inderesTV" text="inderesTV" xmlUrl="https://www.youtube.com/feeds/videos.xml?channel_id=UCA8t6FufzhadZJZBXiYW8jQ" htmlUrl="https://www.youtube.com/channel/UCA8t6FufzhadZJZBXiYW8jQ" type="rss"></outline>
+ <outline title="John Kay" text="John Kay" xmlUrl="https://www.johnkay.com/feed" htmlUrl="https://www.johnkay.com" type="rss"></outline>
+ <outline title="Microeconomic Insights" text="Microeconomic Insights" xmlUrl="https://microeconomicinsights.org/feed/" htmlUrl="https://microeconomicinsights.org" type="rss"></outline>
+ <outline title="SalkunRakentaja" text="SalkunRakentaja" xmlUrl="https://www.salkunrakentaja.fi/feed/rss/" htmlUrl="https://www.salkunrakentaja.fi" type="rss"></outline>
+ <outline title="Stochastic Trend" text="Stochastic Trend" xmlUrl="http://feeds.feedburner.com/StochasticTrend" htmlUrl="http://stochastictrend.blogspot.com/" type="rss"></outline>
+ <outline title="The Demand Side" text="The Demand Side" xmlUrl="https://www.thedemandside.com/blog-feed.xml" htmlUrl="https://www.thedemandside.com/blog" type="rss"></outline>
+ <outline title="Vesa Vihriälä" text="Vesa Vihriälä" xmlUrl="https://www.vesavihriala.fi/feed" htmlUrl="https://www.vesavihriala.fi" type="rss"></outline>
+ <outline title="Wolf Street" text="Wolf Street" xmlUrl="https://wolfstreet.com/feed/" htmlUrl="https://wolfstreet.com" type="rss"></outline>
+ </outline>
+ <outline text="Meditations">
+ <outline title="Andrej Karpathy blog" text="Andrej Karpathy blog" xmlUrl="https://karpathy.github.io/feed.xml" htmlUrl="http://karpathy.github.io/" type="rss"></outline>
+ <outline title="Blog – Hackaday" text="Blog – Hackaday" xmlUrl="http://hackaday.com/blog/feed/" htmlUrl="https://hackaday.com" type="rss"></outline>
+ <outline title="Edge.org" text="Edge.org" xmlUrl="http://www.edge.org/feed" htmlUrl="https://www.edge.org/" type="rss"></outline>
+ <outline title="Evil Mad Scientist Laboratories" text="Evil Mad Scientist Laboratories" xmlUrl="https://www.evilmadscientist.com/feed/" htmlUrl="https://www.evilmadscientist.com" description="Making the world a better place, one Evil Mad Scientist at a time." type="rss"></outline>
+ <outline title="Insight" text="Insight" xmlUrl="https://zeynep.substack.com/feed" htmlUrl="https://zeynep.substack.com" type="rss"></outline>
+ <outline title="LessWrong" text="LessWrong" xmlUrl="https://www.lesswrong.com/feed.xml?view=curated-rss" htmlUrl="https://www.lesswrong.com" type="rss"></outline>
+ <outline title="LessWrong" text="LessWrong" xmlUrl="https://lesswrong.substack.com/feed" htmlUrl="https://lesswrong.substack.com" type="rss"></outline>
+ <outline title="LessWrong" text="LessWrong" xmlUrl="https://www.lesswrong.com/feed.xml?view=frontpage-rss&amp;karmaThreshold=2" htmlUrl="https://www.lesswrong.com" type="rss"></outline>
+ <outline title="LocalLlama" text="LocalLlama" xmlUrl="https://www.reddit.com/r/LocalLLaMA.rss" htmlUrl="https://www.reddit.com/r/LocalLLaMA" type="rss"></outline>
+ <outline title="Nature - Issue - nature.com science feeds" text="Nature - Issue - nature.com science feeds" xmlUrl="http://feeds.nature.com/nature/rss/current" htmlUrl="http://feeds.nature.com/nature/rss/current" type="rss"></outline>
+ <outline title="Stephen Wolfram Blog" text="Stephen Wolfram Blog" xmlUrl="https://writings.stephenwolfram.com/feed/" htmlUrl="https://writings.stephenwolfram.com" type="rss"></outline>
+ <outline title="TED Talks Daily (SD video)" text="TED Talks Daily (SD video)" xmlUrl="http://feeds.feedburner.com/tedtalks_video" htmlUrl="https://www.ted.com/talks" type="rss"></outline>
+ <outline title="The Berkeley Artificial Intelligence Research Blog" text="The Berkeley Artificial Intelligence Research Blog" xmlUrl="https://bair.berkeley.edu/blog/feed.xml" htmlUrl="http://bair.berkeley.edu/blog/" type="rss"></outline>
+ <outline title="The Unofficial Google Data Science Blog" text="The Unofficial Google Data Science Blog" xmlUrl="http://www.unofficialgoogledatascience.com/feeds/posts/default" htmlUrl="http://www.unofficialgoogledatascience.com/" type="rss"></outline>
+ <outline title="vahtera.blog" text="vahtera.blog" xmlUrl="https://www.vahtera.blog/blog-feed.xml" htmlUrl="https://www.vahtera.blog/blog" type="rss"></outline>
+ </outline>
+ <outline text="News">
+ <outline title="Ars Technica" text="Ars Technica" xmlUrl="http://feeds.arstechnica.com/arstechnica/index" htmlUrl="https://arstechnica.com" type="rss"></outline>
+ <outline title="DER SPIEGEL - International" text="DER SPIEGEL - International" xmlUrl="http://www.spiegel.de/international/index.rss" htmlUrl="https://www.spiegel.de/" type="rss"></outline>
+ <outline title="Hacker News" text="Hacker News" xmlUrl="https://news.ycombinator.com/rss" htmlUrl="https://news.ycombinator.com/" type="rss"></outline>
+ <outline title="Helsingin Kaupungin Uutiset" text="Helsingin Kaupungin Uutiset" xmlUrl="https://www.hel.fi/fi/uutiset/rss" htmlUrl="https://www.hel.fi/fi" type="rss"></outline>
+ <outline title="IEEE Spectrum Recent Content full text" text="IEEE Spectrum Recent Content full text" xmlUrl="http://feeds.feedburner.com/IeeeSpectrumFullText" htmlUrl="https://spectrum.ieee.org" type="rss"></outline>
+ <outline title="Neovim" text="Neovim" xmlUrl="https://neovim.io/news.xml" htmlUrl="https://neovim.io/" description="vim out of the box" type="rss"></outline>
+ <outline title="Newsletter - European Parliament" text="Newsletter - European Parliament" xmlUrl="https://www.europarl.europa.eu/rss/doc/newsletters/en.xml" htmlUrl="https://www.europarl.europa.eu/news/en/agenda/briefing" type="rss"></outline>
+ <outline title="Phoronix" text="Phoronix" xmlUrl="http://www.phoronix.com/rss.php" htmlUrl="https://www.phoronix.com/" type="rss"></outline>
+ <outline title="POLITICO" text="POLITICO" xmlUrl="https://www.politico.eu/feed/" htmlUrl="https://www.politico.eu" type="rss"></outline>
+ <outline title="Press releases - European Parliament" text="Press releases - European Parliament" xmlUrl="https://www.europarl.europa.eu/rss/doc/press-releases/en.xml" htmlUrl="https://www.europarl.europa.eu/news/en" type="rss"></outline>
+ <outline title="Slashdot" text="Slashdot" xmlUrl="http://rss.slashdot.org/Slashdot/slashdot" htmlUrl="https://slashdot.org/" type="rss"></outline>
+ <outline title="Yle Uutiset | Pääuutiset" text="Yle Uutiset | Pääuutiset" xmlUrl="https://feeds.yle.fi/uutiset/v1/majorHeadlines/YLE_UUTISET.rss" htmlUrl="https://yle.fi/uutiset/" type="rss"></outline>
+ </outline>
+ <outline text="Podcast">
+ <outline title="Common Sense with Dan Carlin" text="Common Sense with Dan Carlin" xmlUrl="https://feeds.feedburner.com/dancarlin/commonsense?format=xml" htmlUrl="http://www.dancarlin.com" type="rss"></outline>
+ <outline title="Dan Carlin&#39;s Hardcore History" text="Dan Carlin&#39;s Hardcore History" xmlUrl="https://feeds.feedburner.com/dancarlin/history?format=xml" htmlUrl="http://www.dancarlin.com" type="rss"></outline>
+ <outline title="Dan Carlin&#39;s Hardcore History: Addendum" text="Dan Carlin&#39;s Hardcore History: Addendum" xmlUrl="https://dchhaddendum.libsyn.com/rss" htmlUrl="http://dchhaddendum.libsyn.com/website" type="rss"></outline>
+ <outline title="inderesPodi" text="inderesPodi" xmlUrl="http://feeds.soundcloud.com/users/soundcloud:users:319162419/sounds.rss" htmlUrl="http://soundcloud.com/inderes" type="rss"></outline>
+ <outline title="Lex Fridman Podcast" text="Lex Fridman Podcast" xmlUrl="https://lexfridman.com/feed/podcast/" htmlUrl="https://lexfridman.com/podcast" type="rss"></outline>
+ <outline title="Making Sense with Sam Harris" text="Making Sense with Sam Harris" xmlUrl="http://wakingup.libsyn.com/rss" htmlUrl="http://www.samharris.org" type="rss"></outline>
+ <outline title="Microsoft Research" text="Microsoft Research" xmlUrl="https://www.microsoft.com/en-us/research/feed/" htmlUrl="https://www.microsoft.com/en-us/research" type="rss"></outline>
+ <outline title="Politiikkaradio" text="Politiikkaradio" xmlUrl="https://feeds.yle.fi/areena/v1/series/1-1485162.rss?lang=fi&amp;downloadable=true" htmlUrl="https://areena.yle.fi/1-1485162" type="rss"></outline>
+ <outline title="#rahapodi" text="#rahapodi" xmlUrl="http://feeds.soundcloud.com/users/soundcloud:users:156278369/sounds.rss" htmlUrl="https://blogi.nordnet.fi/rahapodi/" type="rss"></outline>
+ <outline title="The Art of Manliness" text="The Art of Manliness" xmlUrl="https://feeds2.feedburner.com/TheArtOfManliness" htmlUrl="https://www.artofmanliness.com/" type="rss"></outline>
+ <outline title="The Fall of Rome Podcast" text="The Fall of Rome Podcast" xmlUrl="https://rss.art19.com/the-fall-of-rome-podcast" htmlUrl="https://fallofromepodcast.wordpress.com/" type="rss"></outline>
+ <outline title="The Peter Attia Drive" text="The Peter Attia Drive" xmlUrl="http://peterattiadrive.libsyn.com/rss" htmlUrl="http://www.PeterAttiaMD.com" type="rss"></outline>
+ <outline title="The Psychology Podcast with Scott Barry Kaufman" text="The Psychology Podcast with Scott Barry Kaufman" xmlUrl="https://anchor.fm/s/2f55bfc0/podcast/rss" htmlUrl="https://anchor.fm/the-psychology-podcast" type="rss"></outline>
+ <outline title="The Tim Ferriss Show" text="The Tim Ferriss Show" xmlUrl="https://tim.blog/feed/" htmlUrl="https://tim.blog/podcast" type="rss"></outline>
+ <outline title="Tides of History" text="Tides of History" xmlUrl="https://rss.art19.com/tides-of-history" htmlUrl="http://wondery.com/shows/tides-of-history/" type="rss"></outline>
+ </outline>
+ <outline text="Programming">
+ <outline title="Devlog" text="Devlog" xmlUrl="https://vonheikemen.github.io/devlog/atom.xml" htmlUrl="https://vonheikemen.github.io/devlog/" type="rss"></outline>
+ <outline title="Hoverbear" text="Hoverbear" xmlUrl="https://hoverbear.org/rss.xml" htmlUrl="https://hoverbear.org" type="rss"></outline>
+ <outline title="Deepmind Latest Post" text="Deepmind Latest Post" xmlUrl="https://deepmind.com/blog/feed/basic/" htmlUrl="https://deepmind.com/blog/" type="rss"></outline>
+ <outline title="Forgejo News" text="Forgejo News" xmlUrl="https://forgejo.org/rss.xml" htmlUrl="https://forgejo.org/" type="rss"></outline>
+ <outline title="Inside Rust Blog" text="Inside Rust Blog" xmlUrl="https://blog.rust-lang.org/inside-rust/feed.xml" htmlUrl="https://blog.rust-lang.org/inside-rust/" type="rss"></outline>
+ <outline title="Mike Blumenkrantz" text="Mike Blumenkrantz" xmlUrl="https://www.supergoodcode.com/feed.xml" htmlUrl="https://www.supergoodcode.com/" type="rss"></outline>
+ <outline title="NixOS Weekly" text="NixOS Weekly" xmlUrl="https://weekly.nixos.org/feeds/all.rss.xml" htmlUrl="https://weekly.nixos.org/" type="rss"></outline>
+ <outline title="Rust Blog" text="Rust Blog" xmlUrl="https://blog.rust-lang.org/feed.xml" htmlUrl="https://blog.rust-lang.org/" type="rss"></outline>
+ <outline title="Vaxry&#39;s Blog" text="Vaxry&#39;s Blog" xmlUrl="https://blog.vaxry.net/feed" htmlUrl="https://blog.vaxry.net/" type="rss"></outline>
+ </outline>
+ <outline text="Software releases">
+ <outline title="matrix.org" text="matrix.org" xmlUrl="https://matrix.org/blog/feed" htmlUrl="https://matrix.org" type="rss"></outline>
+ <outline title="Recent Commits to nixos-config:master" text="Recent Commits to nixos-config:master" xmlUrl="https://github.com/jakubgs/nixos-config/commits/master.atom" htmlUrl="https://github.com/jakubgs/nixos-config/commits/master" type="rss"></outline>
+ <outline title="Release notes from AdGuardHome" text="Release notes from AdGuardHome" xmlUrl="https://github.com/AdguardTeam/AdGuardHome/releases.atom" htmlUrl="https://github.com/AdguardTeam/AdGuardHome/releases" type="rss"></outline>
+ <outline title="Release notes from ashell" text="Release notes from ashell" xmlUrl="https://github.com/MalpenZibo/ashell/releases.atom" htmlUrl="https://github.com/MalpenZibo/ashell/releases" type="rss"></outline>
+ <outline title="Release notes from audiobookshelf" text="Release notes from audiobookshelf" xmlUrl="https://github.com/advplyr/audiobookshelf/releases.atom" htmlUrl="https://github.com/advplyr/audiobookshelf/releases" type="rss"></outline>
+ <outline title="Release notes from esp-hal" text="Release notes from esp-hal" xmlUrl="https://github.com/esp-rs/esp-hal/releases.atom" htmlUrl="https://github.com/esp-rs/esp-hal/releases" type="rss"></outline>
+ <outline title="Release notes from glance" text="Release notes from glance" xmlUrl="https://github.com/glanceapp/glance/releases.atom" htmlUrl="https://github.com/glanceapp/glance/releases" type="rss"></outline>
+ <outline title="Release notes from Hyprland" text="Release notes from Hyprland" xmlUrl="https://github.com/hyprwm/Hyprland/releases.atom" htmlUrl="https://github.com/hyprwm/Hyprland/releases" type="rss"></outline>
+ <outline title="Release notes from lean4" text="Release notes from lean4" xmlUrl="https://github.com/leanprover/lean4/releases.atom" htmlUrl="https://github.com/leanprover/lean4/releases" type="rss"></outline>
+ <outline title="Release notes from llama.cpp" text="Release notes from llama.cpp" xmlUrl="https://github.com/ggerganov/llama.cpp/releases.atom" htmlUrl="https://github.com/ggerganov/llama.cpp/releases" type="rss"></outline>
+ <outline title="Release notes from Miniflux" text="Release notes from Miniflux" xmlUrl="https://github.com/miniflux/v2/releases.atom" htmlUrl="https://github.com/miniflux/v2/releases" type="rss"></outline>
+ <outline title="Release notes from mini.nvim" text="Release notes from mini.nvim" xmlUrl="https://github.com/nvim-mini/mini.nvim/releases.atom" htmlUrl="https://github.com/nvim-mini/mini.nvim/releases" type="rss"></outline>
+ <outline title="Release notes from neovim" text="Release notes from neovim" xmlUrl="https://github.com/neovim/neovim/releases.atom" htmlUrl="https://github.com/neovim/neovim/releases" type="rss"></outline>
+ <outline title="Release notes from nom" text="Release notes from nom" xmlUrl="https://github.com/guyfedwards/nom/releases.atom" htmlUrl="https://github.com/guyfedwards/nom/releases" type="rss"></outline>
+ <outline title="Release notes from shiori" text="Release notes from shiori" xmlUrl="https://github.com/go-shiori/shiori/releases.atom" htmlUrl="https://github.com/go-shiori/shiori/releases" type="rss"></outline>
+ <outline title="Release notes from synapse" text="Release notes from synapse" xmlUrl="https://github.com/element-hq/synapse/releases.atom" htmlUrl="https://github.com/element-hq/synapse/releases" type="rss"></outline>
+ <outline title="Release notes from walker" text="Release notes from walker" xmlUrl="https://github.com/abenz1267/walker/releases.atom" htmlUrl="https://github.com/abenz1267/walker/releases" type="rss"></outline>
+ <outline title="Releases for readeck/readeck" text="Releases for readeck/readeck" xmlUrl="https://codeberg.org/readeck/readeck/releases.rss" htmlUrl="https://codeberg.org/readeck/readeck/release" type="rss"></outline>
+ </outline>
+ </body>
+</opml>
diff --git a/hosts/tammi/iptable-nat.service b/hosts/tammi/iptable-nat.service
new file mode 100644
index 0000000..f5ecd63
--- /dev/null
+++ b/hosts/tammi/iptable-nat.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Apply NAT rules for IPv4
+After=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/hosts/tammi/syntax-highlighting.js b/hosts/tammi/syntax-highlighting.js
new file mode 100644
index 0000000..362994a
--- /dev/null
+++ b/hosts/tammi/syntax-highlighting.js
@@ -0,0 +1,23 @@
+#!/usr/bin/node
+// Server side syntax highlight with Shiki https://shiki.matsu.io/
+// This script is replacement for pygments/highlight for cgit
+// Shiki is installed with `npm install -g shiki`
+// input: filename, stdin - source file. Outputs the highligted html to stdout.
+import { argv, stdin, stdout } from 'node:process';
+import { codeToHtml } from "/usr/lib/node_modules/shiki/dist/index.mjs";
+
+async function highlight(syntax) {
+ stdin.on("data", async (data) => {
+ const text = Buffer.from(data).toString("utf8");
+ const html = await codeToHtml(text, {
+ lang: syntax,
+ theme: "gruvbox-dark-soft"
+ });
+ stdout.write(html);
+ });
+ return true
+}
+
+const filename = argv[1];
+const lang = filename.split(".")[1];
+highlight(lang);
diff --git a/roles/shared.nix b/roles/shared.nix
new file mode 100644
index 0000000..47aaf6f
--- /dev/null
+++ b/roles/shared.nix
@@ -0,0 +1,818 @@
+{
+ pkgs,
+ vars,
+ lib,
+ inputs,
+ config,
+ agenix,
+ ...
+}:
+
+{
+ age = {
+ identityPaths = [ "/home/petri/.ssh/id_ed25519" ];
+ secrets = {
+ s3fs.file = ../secrets/s3fs.age;
+ duckdns.file = ../secrets/duckdns_login_token.age;
+ };
+ };
+
+ nixpkgs.config = {
+ allowUnfree = true;
+ qt.enable = true;
+ };
+ nix = {
+ optimise = {
+ automatic = true;
+ };
+ sshServe = {
+ trusted = true;
+ enable = true;
+ };
+ daemonCPUSchedPolicy = "idle";
+ daemonIOSchedClass = "idle";
+ gc = {
+ automatic = true;
+ };
+ settings = {
+ #substituters = [ "https://nix-community.cachix.org" ];
+ #trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ];
+ trusted-users = [ "${vars.user}" ];
+ auto-optimise-store = true;
+ nix-path = [ "nixpkgs=${pkgs.path}" ];
+ experimental-features = "nix-command flakes";
+ system-features = [
+ "benchmark"
+ "big-parallel"
+ "kvm"
+ "gccarch-x86-64-v3"
+ ];
+ };
+ registry = (lib.mapAttrs (_: flake: { inherit flake; })) (
+ (lib.filterAttrs (_: lib.isType "flake")) inputs
+ );
+ extraOptions = ''
+ http2 = true
+ keep-outputs = true
+ keep-derivations = true
+ '';
+ };
+ system.switch = {
+ enable = false;
+ enableNg = true;
+ };
+
+ system.activationScripts.diff = {
+ supportsDryActivation = true;
+ text = ''
+ ${pkgs.nvd}/bin/nvd --nix-bin-dir=${pkgs.nix}/bin diff \
+ /run/current-system "$systemConfig"
+ '';
+ };
+
+ zramSwap = {
+ enable = true;
+ algorithm = "zstd";
+ };
+
+ boot = {
+ tmp = {
+ tmpfsHugeMemoryPages = "within_size";
+ useTmpfs = true;
+ };
+ binfmt.emulatedSystems = [
+ "aarch64-linux"
+ "riscv64-linux"
+ ];
+ kernel.sysctl = {
+ "kernel.sysrq" = 1;
+ "net.core.netdev_max_backlog" = 25000;
+ "net.core.rmem_default" = 67108864; # allow network stack 64MB
+ "net.core.rmem_max" = 67108864; # allow network stack 64MB
+ "net.core.wmem_default" = 67108864;
+ "net.core.wmem_max" = 67108864;
+ "net.core.default_qdisc" = "fq";
+
+ "net.ipv4.tcp_congestion_control" = "bbr";
+ "net.ipv4.tcp_ecn" = 1;
+ "net.ipv4.tcp_fastopen" = 3;
+ "net.ipv4.tcp_fin_timeout" = 10;
+ "net.ipv4.tcp_low_latency" = 1;
+ "net.ipv4.tcp_mtu_probing" = 2; # recommended for hosts with jumbo frames enabled
+ "net.ipv4.tcp_no_metrics_save" = 1;
+ "net.ipv4.tcp_rmem" = "4096 87380 33554432"; # increase Linux autotuning TCP buffer limit to 32MB
+ "net.ipv4.tcp_slow_start_after_idle" = 0;
+ "net.ipv4.tcp_syncookies" = 1;
+ "net.ipv4.tcp_timestamps" = 1;
+ "net.ipv4.tcp_wmem" = "4096 65536 33554432";
+ "net.ipv6.conf.default.accept_ra" = 2;
+ "net.ipv6.conf.default.router_solicitations" = 3;
+ "net.ipv6.conf.default.router_solicitation_interval" = 1;
+ "net.ipv6.conf.default.router_solicitation_delay" = 0;
+ "net.ipv6.conf.default.dad_transmits" = 1;
+ "net.ipv6.conf.default.accept_dad" = 1;
+ "net.ipv6.conf.default.optimistic_dad" = 1;
+ "net.mptcp.enabled" = 1;
+
+ "vm.nr_hugepages" = 512;
+ "vm.dirty_background_ratio" = 5;
+ "vm.swappiness" = 10;
+ "kernel.sched_wakeup_granularity_ns" = 500000;
+ };
+ loader = {
+ systemd-boot = {
+ memtest86.enable = true;
+ configurationLimit = lib.mkDefault 8;
+ consoleMode = lib.mkDefault "max";
+ enable = true;
+ };
+ efi.canTouchEfiVariables = true;
+ };
+ initrd = {
+ systemd = {
+ enable = true;
+ dbus.enable = true;
+ network = {
+ enable = true;
+ networks."99-dhcp" = {
+ matchConfig.Name = "*";
+ networkConfig.DHCP = "yes";
+ };
+ };
+ };
+ };
+ };
+
+ networking = {
+ nftables.enable = true;
+ useDHCP = false;
+ firewall = {
+ enable = true;
+ allowPing = true;
+ allowedTCPPorts = [
+ 22
+ 443
+ 5353
+ ];
+ };
+ wireless.iwd = {
+ enable = true;
+ settings = {
+ Network = {
+ EnableIPv6 = true;
+ };
+ };
+ };
+ useNetworkd = true;
+ nameservers = [
+ "2001:14ba:a300:e5ba::1#adguard.tammi.cc"
+ "87.92.90.90#adguard.tammi.cc"
+ ];
+ };
+
+ systemd = {
+ user.tmpfiles.users = {
+ ${vars.user}.rules = [ "D %C - - - 7d" ];
+ };
+ tmpfiles.rules = [
+ "d /tmp 1777 root root 10d"
+ "d /run/pam_timestamp 0700 root root -"
+ ];
+ services = {
+ "systemd-networkd".environment.SYSTEMD_LOG_LEVEL = "debug";
+ };
+ extraConfig = ''
+ DefaultTimeoutStopSec=10
+ DefaultLimitNOFILE=2048
+ '';
+ # watchdog = {
+ # device = "/dev/watchdog";
+ # runtimeTime = "30s";
+ #};
+ sleep.extraConfig = ''
+ AllowSuspend=yes
+ AllowHibernation=yes
+ AllowHybridSleep=yes
+ AllowSuspendThenHibernate=yes
+ HibernateDelaySec=3600
+ '';
+
+ services.nix-daemon = {
+ environment.TMPDIR = "/var/tmp";
+ };
+ };
+
+ powerManagement = {
+ enable = true;
+ };
+
+ console = {
+ font = "${pkgs.tamzen}/share/consolefonts/Tamzen8x16.psf";
+ packages = with pkgs; [ tamzen ];
+ earlySetup = true;
+ colors = [
+ "3b4252"
+ "bf616a"
+ "a3be8c"
+ "ebcb8b"
+ "81a1c1"
+ "b48ead"
+ "88c0d0"
+ "e5e9f0"
+ "4c566a"
+ "bf616a"
+ "a3be8c"
+ "ebcb8b"
+ "81a1c1"
+ "b48ead"
+ "8fbcbb"
+ "eceff4"
+ ];
+ keyMap = "us";
+ };
+
+ security = {
+ pam.services = {
+ hyprlock = { };
+ greetd = {
+ enableGnomeKeyring = true;
+ };
+ };
+ polkit = {
+ enable = true;
+ extraConfig = ''
+ polkit.addRule(function(action, subject) {
+ if (subject.isInGroup("wheel") && subject.local) {
+ // Always allow local wheel users without password
+ return polkit.Result.YES;
+ }
+ });
+ '';
+ };
+ rtkit.enable = true;
+ sudo.extraConfig = ''
+ Defaults timestamp_timeout=60
+ '';
+ };
+
+ time.timeZone = "Europe/Helsinki";
+
+ i18n = {
+ defaultLocale = "en_US.UTF-8";
+ extraLocaleSettings = {
+ LC_ADDRESS = "fi_FI.UTF-8";
+ LC_IDENTIFICATION = "fi_FI.UTF-8";
+ LC_MEASUREMENT = "fi_FI.UTF-8";
+ LC_MONETARY = "fi_FI.UTF-8";
+ LC_NAME = "fi_FI.UTF-8";
+ LC_NUMERIC = "fi_FI.UTF-8";
+ LC_PAPER = "fi_FI.UTF-8";
+ LC_TELEPHONE = "fi_FI.UTF-8";
+ LC_TIME = "fi_FI.UTF-8";
+ };
+ extraLocales = [ "fi_FI.UTF-8/UTF-8" ];
+ };
+
+ users.users.${vars.user} = {
+ isNormalUser = true;
+ description = "${vars.name}";
+ extraGroups = [
+ "adm"
+ "audio"
+ "bluetooth"
+ "input"
+ "lp"
+ "network"
+ "plugdev"
+ "render"
+ "systemd-journal"
+ "video"
+ "wheel"
+ "wireshark"
+ "render"
+ ];
+ ignoreShellProgramCheck = true;
+ packages = with pkgs; [
+ alsa-utils
+ blis
+ bluez
+ bluez-tools
+ bluez-alsa
+ glfw
+ glm
+ glxinfo
+ gotop
+ libva
+ libva-utils
+ nixfmt-rfc-style
+ shaderc.static
+ shaderc.dev
+ vulkan-headers
+ vulkan-loader
+ vulkan-tools
+ vulkan-validation-layers
+ ];
+ shell = pkgs.${vars.shell};
+ openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHIXKjutIlv3CS9dmo9bDUUt3UR9O9xUKFXzci3LvNTQ petri.hienonen@gmail.com"
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJzUFgqZF3U6RfbvQLKBdzNgWHGp3z+9FGvqjgTY8N6K petri@saarni"
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIimbOBDKbTNi8b+P0wWaZRszRV59lnokqrGQRIdgqOj petri@kataja"
+ ];
+ };
+
+ environment = {
+ systemPackages = with pkgs; [
+ agenix.packages.${system}.default
+ clinfo
+ coreutils
+ curl
+ git
+ gnumake
+ gnupg
+ greetd.tuigreet
+ linux-firmware
+ lm_sensors
+ mimalloc
+ minio-client
+ neovim
+ nvd
+ procps
+ s3fs
+ sysfsutils
+ unrar
+ unzip
+ usbutils
+ zstd
+ ];
+ variables = {
+ EDITOR = "${pkgs.neovim}/bin/nvim";
+ LD_LIBRARY_PATH = "${pkgs.vulkan-loader}/lib:$LD_LIBRARY_PATH";
+ NIXPKGS_ALLOW_UNFREE = 1;
+ QT_QPA_PLATFORM = "xcb";
+ SYSTEMD_COLORS = 256;
+ SYSTEMD_LOG_COLOR = 1;
+ SYSTEMD_URLIFY = 1;
+ VULKAN_SDK = "${pkgs.vulkan-headers}";
+ };
+ };
+
+ fileSystems."/media/llm" = {
+ device = "llm";
+ fsType = "fuse./run/current-system/sw/bin/s3fs";
+ noCheck = true;
+ options = [
+ "_netdev"
+ "allow_other"
+ "umask=0007"
+ "uid=1000"
+ "use_path_request_style"
+ "url=https://s3.tammi.cc"
+ "passwd_file=${config.age.secrets.s3fs.path}"
+ ];
+ };
+
+ fileSystems."/media/skydrive" = {
+ device = "skydrive";
+ fsType = "fuse./run/current-system/sw/bin/s3fs";
+ noCheck = true;
+ options = [
+ "_netdev"
+ "allow_other"
+ "umask=0007"
+ "uid=1000"
+ "use_path_request_style"
+ "url=https://s3.tammi.cc"
+ "passwd_file=${config.age.secrets.s3fs.path}"
+ ];
+ };
+
+ fonts = {
+ enableDefaultPackages = true;
+ packages = with pkgs; [
+ nerd-fonts.iosevka
+ nerd-fonts.iosevka-term
+ nerd-fonts.zed-mono
+ nixos-icons
+ roboto-serif
+ roboto-mono
+ roboto-flex
+ twitter-color-emoji
+ ];
+ fontDir.enable = true;
+ fontconfig = {
+ enable = true;
+ hinting = {
+ enable = true;
+ autohint = true;
+ style = "medium";
+ };
+ useEmbeddedBitmaps = true;
+ cache32Bit = true;
+ antialias = true;
+ subpixel = {
+ rgba = lib.mkDefault "rgb";
+ lcdfilter = "default";
+ };
+ defaultFonts = {
+ serif = [ "Roboto Serif" ];
+ sansSerif = [ "Roboto Flex" ];
+ monospace = [ "Iosevka Nerd Font" ];
+ emoji = [ "Twitter Color Emoji" ];
+ };
+ };
+ };
+
+ programs = {
+ ssh = {
+ extraConfig = ''
+ Compression = yes
+ ControlMaster = yes
+ Protocol = 2
+ ServerAliveInterval = 1
+ ServerAliveCountMax = 3
+ TCPKeepAlive = yes
+ '';
+ hostKeyAlgorithms = [
+ "ssh-ed25519-cert-v01@openssh.com"
+ "ssh-rsa-cert-v01@openssh.com"
+ "ssh-ed25519"
+ "ssh-rsa"
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+ "ecdsa-sha2-nistp521"
+ "ecdsa-sha2-nistp384"
+ "ecdsa-sha2-nistp256"
+ ];
+ kexAlgorithms = [
+ "curve25519-sha256@libssh.org"
+ "ecdh-sha2-nistp521"
+ "ecdh-sha2-nistp384"
+ "ecdh-sha2-nistp256"
+ "diffie-hellman-group-exchange-sha256"
+ ];
+ macs = [
+ "hmac-sha2-512-etm@openssh.com"
+ "hmac-sha2-256-etm@openssh.com"
+ "umac-128-etm@openssh.com"
+ "hmac-sha2-512"
+ "hmac-sha2-256"
+ "umac-128@openssh.com"
+ ];
+ ciphers = [
+ "chacha20-poly1305@openssh.com"
+ "aes256-gcm@openssh.com"
+ "aes128-gcm@openssh.com"
+ "aes256-ctr"
+ "aes192-ctr"
+ "aes128-ctr"
+ ];
+
+ };
+ arp-scan.enable = true;
+ appimage = {
+ enable = true;
+ binfmt = true;
+ };
+ ccache.enable = true;
+ wireshark.enable = true;
+ command-not-found.enable = false; # enabled by default, does not work with flakes
+ dconf.enable = true; # needed by gnome application, such as firefox
+ mosh = {
+ enable = true;
+ withUtempter = true;
+ openFirewall = true;
+ };
+ xwayland.enable = true;
+ gnupg.agent = {
+ enable = true;
+ enableSSHSupport = true;
+ };
+ alvr = {
+ enable = true;
+ openFirewall = true;
+ };
+ gamescope = {
+ enable = true;
+ };
+ steam = {
+ enable = true;
+ gamescopeSession.enable = true;
+ remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
+ dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
+ localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Ga
+ extraCompatPackages = [ pkgs.proton-ge-bin ];
+ };
+ };
+
+ systemd.network.networks."50-bluetooth-pan" = {
+ matchConfig.Name = "bnep*";
+ networkConfig = {
+ DHCP = true;
+ IPv6AcceptRA = true;
+ };
+ };
+
+ hardware = {
+ enableAllFirmware = true;
+ enableRedistributableFirmware = true;
+ graphics = {
+ enable = true;
+ };
+ bluetooth = {
+ enable = true;
+ powerOnBoot = true;
+ settings = {
+ General = {
+ ControllerMode = "bredr";
+ DiscoverableTimeout = 0;
+ Experimental = true;
+ FastConnectable = true;
+ JustWorksRepairing = "always";
+ KernelExperimental = true;
+ MultiProfile = "multiple";
+ Privacy = "off";
+ Testing = true;
+ };
+ Policy = {
+ AutoEnable = true;
+ };
+ LE = {
+ ConnectionLatency = 0;
+ ConnectionSupervisionTimeout = 2000;
+ EnableAdvMonInterleaveScan = 1;
+ MaxConnectionInterval = 16;
+ MinConnectionInterval = 16;
+ ScanIntervalAutoConnect = 300;
+ };
+ GATT = {
+ Channels = 6;
+ };
+ };
+ };
+ };
+
+ services = {
+ pipewire = {
+ enable = true;
+ alsa.enable = true;
+ pulse = {
+ enable = true;
+
+ };
+ audio.enable = true;
+ wireplumber = {
+ enable = true;
+ extraConfig = {
+ "10-bluez" = {
+ "monitor.bluez.properties" = {
+ "bluez5.enable-sbc-xq" = true;
+ "bluez5.enable-msbc" = true;
+ "bluez5.enable-hw-volume" = true;
+ "bluez5.roles" = [
+ "hsp_hs"
+ "hsp_ag"
+ "hfp_hf"
+ "hfp_ag"
+ ];
+ };
+ };
+ };
+ };
+ extraConfig.pipewire-pulse = {
+ "switch-on-connect.conf" = {
+ "pulse.cmd" = [
+ {
+ cmd = "load-module";
+ args = "module-switch-on-connect";
+ }
+ ];
+ };
+ };
+ };
+ libinput = {
+ enable = true;
+ };
+ geoclue2 = {
+ package = pkgs.geoclue2-with-demo-agent;
+ enableDemoAgent = true;
+ enable = true;
+ };
+ systembus-notify.enable = true;
+ dbus = {
+ implementation = "broker";
+ packages = [ pkgs.bluez ];
+ };
+ timesyncd = {
+ enable = true;
+ servers = [
+ "time1.mikes.fi"
+ "time2.mikes.fi"
+ ];
+ };
+ printing.enable = true;
+ upower = {
+ enable = true;
+ };
+ devmon.enable = true; # automatic device mounting daemon
+ journald = {
+ audit = true;
+ extraConfig = ''
+ ReadKMsg=1
+ SystemMaxUse=200M
+ '';
+ };
+ xserver = {
+ enable = true; # Required by steam
+ xkb = {
+ layout = "us";
+ variant = "";
+ };
+ updateDbusEnvironment = true;
+ };
+ udev.extraRules = ''
+ # Blinkstick nano
+ SUBSYSTEM=="usb", ATTR{idVendor}=="20a0", ATTR{idProduct}=="41e5", MODE:="0666"
+ # ESP32 waveshare
+ SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="55d3", MODE:="0666"
+ '';
+ getty.autologinUser = "${vars.user}";
+ kmscon = {
+ enable = true;
+ hwRender = true;
+ extraOptions = "--xkb-layout=fi";
+ fonts = [
+ {
+ name = "Iosevka Nerd Font";
+ package = pkgs.nerd-fonts.iosevka;
+ }
+ ];
+ };
+ openssh = {
+ enable = true;
+ openFirewall = true;
+ settings = {
+ UseDns = true;
+ AllowUsers = [ "petri" ];
+ KexAlgorithms = [
+ "curve25519-sha256@libssh.org"
+ "ecdh-sha2-nistp521"
+ "ecdh-sha2-nistp384"
+ "ecdh-sha2-nistp256"
+ "diffie-hellman-group-exchange-sha256"
+ ];
+ Ciphers = [
+ "chacha20-poly1305@openssh.com"
+ "aes256-gcm@openssh.com"
+ "aes128-gcm@openssh.com"
+ "aes256-ctr"
+ "aes192-ctr"
+ "aes128-ctr"
+ ];
+ Macs = [
+ "hmac-sha2-512-etm@openssh.com"
+ "hmac-sha2-256-etm@openssh.com"
+ "umac-128-etm@openssh.com"
+ "hmac-sha2-512"
+ "hmac-sha2-256"
+ "umac-128@openssh.com"
+ ];
+ PermitRootLogin = "no";
+ PasswordAuthentication = false;
+ };
+ extraConfig = ''
+ ClientAliveCountMax 0
+ ClientAliveInterval 300
+
+ LoginGraceTime 60
+ AllowTcpForwarding no
+ AllowAgentForwarding no
+ MaxAuthTries 3
+ MaxSessions 2
+ TCPKeepAlive no
+ '';
+ };
+ earlyoom = {
+ enableNotifications = true;
+ enable = true;
+ };
+ ananicy = {
+ enable = true;
+ package = pkgs.ananicy-cpp;
+ rulesProvider = pkgs.ananicy-rules-cachyos;
+ };
+ fwupd = {
+ enable = true;
+ extraRemotes = [ "lvfs-testing" ];
+ };
+ resolved = {
+ enable = true;
+ dnssec = "true";
+ llmnr = "true";
+ dnsovertls = "true";
+ domains = [ "~." ];
+ extraConfig = ''
+ MulticastDNS=true
+ Cache=no-negative
+ '';
+ fallbackDns = [
+ "2606:4700:4700::1111#cloudflare-dns.com"
+ "2606:4700:4700::1001#cloudflare-dns.com"
+ "1.1.1.1#cloudflare-dns.com"
+ "1.0.0.1#cloudflare-dns.com"
+ "9.9.9.9#dns.quad9.net"
+ "149.112.112.112#dns.quad9.net"
+ ];
+ };
+ fstrim.enable = true;
+ smartd = {
+ notifications = {
+ test = true;
+ systembus-notify.enable = true;
+ };
+ enable = true;
+ };
+ greetd = {
+ enable = true;
+ vt = 1;
+ restart = true;
+ settings = {
+ initial_session = {
+ command = "/etc/greetd/hyprland-wrapper.sh";
+ user = "${vars.user}";
+ };
+ default_session = {
+ command = "${pkgs.greetd.tuigreet}/bin/tuigreet --cmd /etc/greetd/hyprland-wrapper.sh";
+ };
+ };
+ };
+ };
+
+ environment.etc = {
+ "greetd/hyprland-wrapper.sh" = {
+ text = ''
+ #!/bin/sh
+ # Session
+ export XDG_SESSION_TYPE=wayland
+ export XDG_SESSION_DESKTOP=Hyprland
+ export XDG_CURRENT_DESKTOP=Hyprland
+
+ # Wayland stuff
+ export MOZ_ENABLE_WAYLAND=1
+ export QT_QPA_PLATFORM=wayland
+ export SDL_VIDEODRIVER=wayland
+
+ dbus-run-session ${pkgs.hyprland}/bin/Hyprland
+ '';
+ mode = "0755";
+ };
+ "bluetooth-tether.sh" = {
+ mode = "0755";
+ text = ''
+ #!/bin/sh
+ DEVICE_MAC="3C:01:EF:D9:0D:96" # Phone shares the internet
+ ADAPTER="hci0" # Replace if your adapter is different (check with `bluetoothctl list`)
+
+ # Ensure Bluetooth is powered on
+ ${pkgs.bluez}/bin/bluetoothctl power on
+
+ # Scan for the device briefly (5 seconds)
+ ${pkgs.bluez}/bin/bluetoothctl scan on &
+ sleep 5
+ ${pkgs.bluez}/bin/bluetoothctl scan off
+
+ # Check if the device is present
+ if ${pkgs.bluez}/bin/bluetoothctl devices | grep -q "$DEVICE_MAC"; then
+ echo "Device $DEVICE_MAC found, attempting to connect to NAP..."
+ # Ensure device is trusted (optional, for reliability)
+ ${pkgs.bluez}/bin/bluetoothctl trust "$DEVICE_MAC"
+ # Connect to NAP profile
+ ${pkgs.dbus}/bin/dbus-send --system --type=method_call --dest=org.bluez \
+ /org/bluez/$ADAPTER/dev_$(echo $DEVICE_MAC | tr ':' '_') \
+ org.bluez.Network1.Connect string:'nap' || {
+ echo "Failed to connect to NAP for $DEVICE_MAC"
+ exit 1
+ }
+ echo "Connected to NAP for $DEVICE_MAC"
+ else
+ echo "Device $DEVICE_MAC not found, skipping connection"
+ exit 0
+ fi
+ '';
+ };
+ };
+
+ systemd.services.bluetooth-tether = {
+ description = "Auto-connect to Bluetooth tethering PAN if device is detected";
+ after = [ "bluetooth.service" ];
+ partOf = [ "bluetooth.service" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ Type = "oneshot";
+ ExecStart = "/etc/bluetooth-tether.sh";
+ RemainAfterExit = true;
+ Restart = "on-failure";
+ RestartSec = "30s"; # Retry every 30 seconds if the device isn't found
+ };
+ };
+}
diff --git a/roles/wallpaper.nix b/roles/wallpaper.nix
new file mode 100644
index 0000000..3cc4f91
--- /dev/null
+++ b/roles/wallpaper.nix
@@ -0,0 +1,23 @@
+{ pkgs, ... }:
+
+{
+ systemd.user.services.wallpaper = {
+ description = "wallpaper";
+ script = ''
+ ${pkgs.hyprland}/bin/hyprctl hyprpaper wallpaper ,$(find -L /etc/nixos/home/wallpapers -type f | shuf -n 1)
+ '';
+ serviceConfig = {
+ Type = "oneshot";
+ };
+ };
+
+ systemd.user.timers.wallpaper = {
+ wantedBy = [ "timers.target" ];
+ timerConfig = {
+ OnBootSec = "10m";
+ OnUnitActiveSec = "10m";
+ Unit = "wallpaper.service";
+ Persistent = true;
+ };
+ };
+}
diff --git a/secrets/duckdns_login_token.age b/secrets/duckdns_login_token.age
new file mode 100644
index 0000000..a290de2
--- /dev/null
+++ b/secrets/duckdns_login_token.age
Binary files differ
diff --git a/secrets/gmail.age b/secrets/gmail.age
new file mode 100644
index 0000000..f9a3540
--- /dev/null
+++ b/secrets/gmail.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 SIk9MQ tG2XMK92K0BS4+6D/7f2FFnhx/XFcn0bc0qryrLZnkI
+Dbl424Y2izE9qTENo0oC+KA7arK3jxs1sCmolpO7L4I
+-> ssh-ed25519 nivnYg xhC2zowQQPKWIJFphgHCWK6Fv3or4gEWMmrq+Xt+wl4
+6PoyXh+MZ3uL9eds8R2w50o9qb+i0elB7eunKMzJImE
+-> ssh-ed25519 egf4NA hceSfAb5vAGGor4zXLtQQrD64HygN13KTye6b58BFT0
+ua1LO4Y7gYhNhMGJ9nAG/f0OtQw7WygPoUsIGKTLee0
+--- AtIO61YC+CUcWLsk/3d8QiCOJo5NTnIn78cFg0MMiNg
+^ؙ 04ɭFDc봮D'('ჲJk \ No newline at end of file
diff --git a/secrets/miniflux_api_key.age b/secrets/miniflux_api_key.age
new file mode 100644
index 0000000..2f53644
--- /dev/null
+++ b/secrets/miniflux_api_key.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 SIk9MQ BRp0yTco5Q4sOac5hFEUhBjaHppmvCTbXlduXRfJAjM
+/tzdOv1JdMS/+wPEG14Os2e+msjTB0kE/BPBrCWeCUc
+-> ssh-ed25519 egf4NA wLPwP/TzjstOjWp0YuM0qbPfjXBfW6vzI9RY6Kj3X2g
+XjjDmWVo9I+cat4FnEIAiyOwXe5aG0AJW65m++Elby0
+-> ssh-ed25519 nivnYg 20vbXcFIy2mHpfVdwu/cQWydnJ4VGrETDEs1sjiKQD4
+x6dD/2btPNAyEU1oNFiBkLw9+I1bqU88NzJSuE0RNyI
+--- xFZP/xTVMWe4RwhNB+RyjcNrkwVEPNpnzExx5uNuiOw
+2$|*)lP<ɢi/dRzZf{t҃4ŚAD&ړ \ No newline at end of file
diff --git a/secrets/openai_auth_token.age b/secrets/openai_auth_token.age
new file mode 100644
index 0000000..0573d6d
--- /dev/null
+++ b/secrets/openai_auth_token.age
Binary files differ
diff --git a/secrets/relesoft.age b/secrets/relesoft.age
new file mode 100644
index 0000000..1cd16d5
--- /dev/null
+++ b/secrets/relesoft.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 SIk9MQ hWfgfvibzmIpL5CVharIXW2qX6NN4ti/IWaxNBfCOlA
+BJ2KsVFx/1Ra/gUfFSGqDH2xGtj3ZUCR2JtO9upObCM
+-> ssh-ed25519 nivnYg +xvAmrXobATO5hswYotmBrJh40RpCi8UIQ8JBl1YVG4
+Q11BE4UCgmv/c9VQeUuKa97J/JmWB4v+YoxHIa9/EQE
+-> ssh-ed25519 egf4NA IgVPAbf5pz/WvI7uI56VKAhtQbCm0A6R8e1QC1nXE0c
+EGb0U+QxTREl4HhWxMLMZcqFsRKEXMqZRRSKdEhdYUU
+--- +Sgmew6pn5ttSrqMwGIeWYZ1chQGUzDTRGvHJmBkDNA
+?q8UgьeCsЗTEp'ZKo \ No newline at end of file
diff --git a/secrets/relesoft_cargo_token.age b/secrets/relesoft_cargo_token.age
new file mode 100644
index 0000000..b57354a
--- /dev/null
+++ b/secrets/relesoft_cargo_token.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 SIk9MQ 6JF0u8WRDM26eTMxB8RHy7gHGD5DyVQO04BUjI8FKFI
+rUSvDpdnbIjeDssPhJ4y3jiuwXfkda42gpRlb5MwZVE
+-> ssh-ed25519 egf4NA 2M5u3dE0K0IODav88dkVu3TAfsv1upra3+TtY6eFYFM
+gFcQyRADiG0ruf7OLBrUxOiXMkptpsm7Zo770xdtAnA
+-> ssh-ed25519 nivnYg OucwYuH7Z6Ih+lUGEPSs1oh/ZsuqrDHttNVyzR5iXnM
+zavvC+0FtsSAVMcpang0rY7MvWqEG92RZOdozOljb1s
+--- LFI08syDoP7BhDymUtGSj26rlvlAIywYrDKs3Ff30N0
+eRBcT|m߹x*!VVn%ï0t([>ŴKyn.:Ԋ_45fh:l \ No newline at end of file
diff --git a/secrets/s3fs.age b/secrets/s3fs.age
new file mode 100644
index 0000000..49e782e
--- /dev/null
+++ b/secrets/s3fs.age
Binary files differ
diff --git a/secrets/secrets.nix b/secrets/secrets.nix
new file mode 100644
index 0000000..59159ec
--- /dev/null
+++ b/secrets/secrets.nix
@@ -0,0 +1,21 @@
+let
+ katajaHostPub = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJzUFgqZF3U6RfbvQLKBdzNgWHGp3z+9FGvqjgTY8N6K petri@saarni";
+ pihlajaHostPub = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIimbOBDKbTNi8b+P0wWaZRszRV59lnokqrGQRIdgqOj petri@kataja";
+ saarniHostPub = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHIXKjutIlv3CS9dmo9bDUUt3UR9O9xUKFXzci3LvNTQ petri.hienonen@gmail.com";
+ systems = [
+ katajaHostPub
+ pihlajaHostPub
+ saarniHostPub
+ ];
+in
+{
+ "duckdns_login_token.age".publicKeys = systems;
+ "gmail.age".publicKeys = systems;
+ "miniflux_api_key.age".publicKeys = systems;
+ "openai_auth_token.age".publicKeys = systems;
+ "relesoft.age".publicKeys = systems;
+ "relesoft_cargo_token.age".publicKeys = systems;
+ "s3fs.age".publicKeys = systems;
+ "shiori_password.age".publicKeys = systems;
+ "unsplash_access_key.age".publicKeys = systems;
+}
diff --git a/secrets/shiori_password.age b/secrets/shiori_password.age
new file mode 100644
index 0000000..ab0f3c2
--- /dev/null
+++ b/secrets/shiori_password.age
@@ -0,0 +1,10 @@
+age-encryption.org/v1
+-> ssh-ed25519 SIk9MQ NH850utZIMxFFNAZCZHsyjL5f+t3TnhThtVnExWpEHk
+uYEf+jtdqZcr39Yj2vmBZap6lUVdHKSV3O0MYwFk2Xc
+-> ssh-ed25519 egf4NA bUcc1F73WG3Jc5BxjG8L4w88uJsY+SbrXO5Yb1ZMzEw
+uAiwubSlvXV+hH2qgQEEUNB69uEVISRgQzluIoV80Jc
+-> ssh-ed25519 nivnYg 4Bzp4S/J17xt3pG8L70Kw5rtBjL+Q9r9mnjSMw6FV30
+TALKdu7Xn+p/p6aUifBCoEzian4IEWwYbx7F7X2CLBE
+--- yD8ik0HkBt4IcbZxuH5R7Jqk2mqyCOfzqQ+WkI5Orv4
+Q@Øwg7#B)
+U'B_λQy
diff --git a/secrets/unsplash_access_key.age b/secrets/unsplash_access_key.age
new file mode 100644
index 0000000..72dd04c
--- /dev/null
+++ b/secrets/unsplash_access_key.age
Binary files differ