mirror of
https://codeberg.org/muon/home.git
synced 2026-07-03 23:49:35 +00:00
Merge branch 'main' of codeberg.org:muon/home
This commit is contained in:
commit
87f09972b0
47 changed files with 2603 additions and 136 deletions
|
|
@ -3,6 +3,7 @@ keys:
|
|||
- &muho age1v4s4hg7u3vjjkarvrk7v6ev7w3wja2r5xm7f4t06culw3fuq7qns8sfju7
|
||||
- &mups age1n7qz2w3hkf7fcdv92kxw9k6uef487na2tlc87486rcjwj8lyfuws5q46gn
|
||||
- &murk age1mgjhkqy9x27gv2t2xvq46dxcajkr9c8zes7rr3dj0ac7md2j6vas43dftp
|
||||
- &musk age1m97a3eptxwpdd7h5kkqe9gkmhg6rquc64qjmlsfqfhfqv8q72crqrylhgc
|
||||
|
||||
creation_rules:
|
||||
- path_regex: modules/nixos/sops/secrets.ya?ml$
|
||||
|
|
@ -12,6 +13,7 @@ creation_rules:
|
|||
- *muho
|
||||
- *mups
|
||||
- *murk
|
||||
- *musk
|
||||
|
||||
- path_regex: modules/home/sops/secrets.ya?ml$
|
||||
key_groups:
|
||||
|
|
@ -20,3 +22,4 @@ creation_rules:
|
|||
- *muho
|
||||
- *mups
|
||||
- *murk
|
||||
- *musk
|
||||
|
|
|
|||
102
flake.lock
generated
102
flake.lock
generated
|
|
@ -139,11 +139,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760948891,
|
||||
"narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=",
|
||||
"lastModified": 1769996383,
|
||||
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04",
|
||||
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -233,11 +233,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768598210,
|
||||
"narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=",
|
||||
"lastModified": 1773367248,
|
||||
"narHash": "sha256-FFMc1uAwy2GYasd0rdNDVxKyAgzuoJH2M+GglBQbqf0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "c47b2cc64a629f8e075de52e4742de688f930dc6",
|
||||
"rev": "be0c641a6a5564caa33982faa1fe2c60d92131c7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -254,11 +254,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1747978958,
|
||||
"narHash": "sha256-pQQnbxWpY3IiZqgelXHIe/OAE/Yv4NSQq7fch7M6nXQ=",
|
||||
"lastModified": 1768598210,
|
||||
"narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "7419250703fd5eb50e99bdfb07a86671939103ea",
|
||||
"rev": "c47b2cc64a629f8e075de52e4742de688f930dc6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -275,11 +275,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767104570,
|
||||
"narHash": "sha256-GKgwu5//R+cLdKysZjGqvUEEOGXXLdt93sNXeb2M/Lk=",
|
||||
"lastModified": 1772330611,
|
||||
"narHash": "sha256-UZjPc/d5XRxvjDbk4veAO4XFdvx6BUum2l40V688Xq8=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "e4e78a2cbeaddd07ab7238971b16468cc1d14daf",
|
||||
"rev": "58fd7ff0eec2cda43e705c4c0585729ec471d400",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -294,11 +294,11 @@
|
|||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767822991,
|
||||
"narHash": "sha256-iyrn9AcPZCoyxX4OT8eMkBsjG7SRUQXXS/V1JzxS7rA=",
|
||||
"lastModified": 1769548169,
|
||||
"narHash": "sha256-03+JxvzmfwRu+5JafM0DLbxgHttOQZkUtDWBmeUkN8Y=",
|
||||
"owner": "nix-community",
|
||||
"repo": "impermanence",
|
||||
"rev": "82e5bc4508cab9e8d5a136626276eb5bbce5e9c5",
|
||||
"rev": "7b1d382faf603b6d264f58627330f9faa5cba149",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -309,11 +309,11 @@
|
|||
},
|
||||
"mnw": {
|
||||
"locked": {
|
||||
"lastModified": 1758834834,
|
||||
"narHash": "sha256-Y7IvY4F8vajZyp3WGf+KaiIVwondEkMFkt92Cr9NZmg=",
|
||||
"lastModified": 1770419553,
|
||||
"narHash": "sha256-b1XqsH7AtVf2dXmq2iyRr2NC1yG7skY7Z6N2MpWHlK4=",
|
||||
"owner": "Gerg-L",
|
||||
"repo": "mnw",
|
||||
"rev": "cfbc7d1cc832e318d0863a5fc91d940a96034001",
|
||||
"rev": "2aaffa8030d0b262176146adbb6b0e6374ce2957",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -330,15 +330,16 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765720983,
|
||||
"narHash": "sha256-tWtukpABmux6EC/FuCJEgA1kmRjcRPtED44N+GGPq+4=",
|
||||
"lastModified": 1768214250,
|
||||
"narHash": "sha256-hnBZDQWUxJV3KbtvyGW5BKLO/fAwydrxm5WHCWMQTbw=",
|
||||
"owner": "feel-co",
|
||||
"repo": "ndg",
|
||||
"rev": "f399ace8bb8e1f705dd8942b24d207aa4d75c936",
|
||||
"rev": "a6bd3c1ce2668d096e4fdaaa03ad7f03ba1fbca8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "feel-co",
|
||||
"ref": "refs/tags/v2.6.0",
|
||||
"repo": "ndg",
|
||||
"type": "github"
|
||||
}
|
||||
|
|
@ -350,11 +351,11 @@
|
|||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768475717,
|
||||
"narHash": "sha256-185VOlWF4K9gzwr7M56ArjqDt6beN/5TxCYLEyVPOcs=",
|
||||
"lastModified": 1771150922,
|
||||
"narHash": "sha256-+oQJun4CFDlOQRocbZpqQDj7agoy56/4ZjT1oUR7NOs=",
|
||||
"owner": "thiagokokada",
|
||||
"repo": "nix-alien",
|
||||
"rev": "a579610c67dc946f39c2a64656699eb29eb2ffb5",
|
||||
"rev": "96045e886ba0dd45b27590e7c0c6e77bbb54033d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -387,11 +388,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765267181,
|
||||
"narHash": "sha256-d3NBA9zEtBu2JFMnTBqWj7Tmi7R5OikoU2ycrdhQEws=",
|
||||
"lastModified": 1771130777,
|
||||
"narHash": "sha256-UIKOwG0D9XVIJfNWg6+gENAvQP+7LO46eO0Jpe+ItJ0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-index-database",
|
||||
"rev": "82befcf7dc77c909b0f2a09f5da910ec95c5b78f",
|
||||
"rev": "efec7aaad8d43f8e5194df46a007456093c40f88",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -422,11 +423,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1748026106,
|
||||
"narHash": "sha256-6m1Y3/4pVw1RWTsrkAK2VMYSzG4MMIj7sqUy7o8th1o=",
|
||||
"lastModified": 1768564909,
|
||||
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "063f43f2dbdef86376cc29ad646c45c46e93234c",
|
||||
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -438,11 +439,11 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1768305791,
|
||||
"narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=",
|
||||
"lastModified": 1771008912,
|
||||
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e",
|
||||
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -470,11 +471,11 @@
|
|||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1768564909,
|
||||
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||
"lastModified": 1773282481,
|
||||
"narHash": "sha256-b/GV2ysM8mKHhinse2wz+uP37epUrSE+sAKXy/xvBY4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||
"rev": "fe416aaedd397cacb33a610b33d60ff2b431b127",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -537,16 +538,15 @@
|
|||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767628834,
|
||||
"narHash": "sha256-qiPFYDicHq4/ji0/9QxVM8hhjspsJrYcMR/S3zKlfjQ=",
|
||||
"owner": "thamenato",
|
||||
"lastModified": 1773343795,
|
||||
"narHash": "sha256-0+HEuOytpwyPt7i1jj6v2QJ+NXXisCYnL2XNwPBltvg=",
|
||||
"owner": "NotAShelf",
|
||||
"repo": "nvf",
|
||||
"rev": "7161c8d857cf7c641433cc750a1a3666f82a3ff0",
|
||||
"rev": "83b44eaf50b96bd5d06b1a56a3a51f1b2362db52",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "thamenato",
|
||||
"ref": "fix-nvim-treesitter",
|
||||
"owner": "NotAShelf",
|
||||
"repo": "nvf",
|
||||
"type": "github"
|
||||
}
|
||||
|
|
@ -573,11 +573,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768481291,
|
||||
"narHash": "sha256-NjKtkJraCZEnLHAJxLTI+BfdU//9coAz9p5TqveZwPU=",
|
||||
"lastModified": 1773096132,
|
||||
"narHash": "sha256-M3zEnq9OElB7zqc+mjgPlByPm1O5t2fbUrH3t/Hm5Ag=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "e085e303dfcce21adcb5fec535d65aacb066f101",
|
||||
"rev": "d1ff3b1034d5bab5d7d8086a7803c5a5968cd784",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -626,11 +626,11 @@
|
|||
"tinted-zed": "tinted-zed"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768603455,
|
||||
"narHash": "sha256-ih6dYNhX1oSg0emfSAvf3iRcgsJtMmS6RUaoCX8kNoU=",
|
||||
"lastModified": 1772296853,
|
||||
"narHash": "sha256-pAtzPsgHRKw/2Kv8HgAjSJg450FDldHPWsP3AKG/Xj0=",
|
||||
"owner": "danth",
|
||||
"repo": "stylix",
|
||||
"rev": "590e5c68c4d5e8c766420473c0185d75113f653b",
|
||||
"rev": "c4b8e80a1020e09a1f081ad0f98ce804a6e85acf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -794,11 +794,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768638486,
|
||||
"narHash": "sha256-+LC0wOiliUXbIj6zT2hCoOQ0zn33BD2NxGoy0QqP3Eo=",
|
||||
"lastModified": 1773290887,
|
||||
"narHash": "sha256-L1yMYmFffHfZNP+hKJGRBmrFKkn/VDhu7jEbVftBQuM=",
|
||||
"owner": "0xc000022070",
|
||||
"repo": "zen-browser-flake",
|
||||
"rev": "76bbc35c59419b8b0616fb779ce5600e85edab11",
|
||||
"rev": "9346698c4562819f61b4e5097151ec0b17729fab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
50
flake.nix
50
flake.nix
|
|
@ -14,8 +14,7 @@
|
|||
nix-alien.url = "github:thiagokokada/nix-alien";
|
||||
|
||||
nvf = {
|
||||
# url = "github:NotAShelf/nvf";
|
||||
url = "github:thamenato/nvf/fix-nvim-treesitter";
|
||||
url = "github:NotAShelf/nvf";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
|
|
@ -47,35 +46,32 @@
|
|||
};
|
||||
|
||||
utils = import ./utils.nix {inherit inputs system sources;};
|
||||
|
||||
# Discover hosts: all subdirectories of hosts/
|
||||
hosts = builtins.attrNames (nixpkgs.lib.filterAttrs
|
||||
(_: type: type == "directory")
|
||||
(builtins.readDir ./hosts));
|
||||
|
||||
nixosConfigs = builtins.listToAttrs (map (host: {
|
||||
name = host;
|
||||
value = utils.mkHost ./hosts/${host}/configuration.nix;
|
||||
}) hosts);
|
||||
in {
|
||||
nixosConfigurations = {
|
||||
# desktop
|
||||
muon = utils.mkHost ./hosts/muon/configuration.nix;
|
||||
|
||||
# laptop
|
||||
muop = utils.mkHost ./hosts/muop/configuration.nix;
|
||||
|
||||
# server
|
||||
muho = utils.mkHost ./hosts/muho/configuration.nix;
|
||||
|
||||
# vps
|
||||
mups = utils.mkHost ./hosts/mups/configuration.nix;
|
||||
|
||||
# vm
|
||||
muvm = utils.mkHost ./hosts/muvm/configuration.nix;
|
||||
|
||||
# work
|
||||
murk = utils.mkHost ./hosts/murk/configuration.nix;
|
||||
|
||||
# lenovo
|
||||
muvo = utils.mkHost ./hosts/muvo/configuration.nix;
|
||||
|
||||
# installer
|
||||
muin = utils.mkHost ./hosts/muin/configuration.nix;
|
||||
};
|
||||
nixosConfigurations = nixosConfigs;
|
||||
|
||||
homeManagerModules.default = ./modules/home;
|
||||
|
||||
# Standalone HM configurations — one per host.
|
||||
# osConfig is injected so all modules using it continue to work.
|
||||
# Use: home-manager switch --flake '.#muon@<host>'
|
||||
homeConfigurations = builtins.listToAttrs (map (host: {
|
||||
name = "muon@${host}";
|
||||
value = utils.mkHome {
|
||||
hostConfig = nixosConfigs.${host};
|
||||
homeFile = ./hosts/${host}/home.nix;
|
||||
};
|
||||
}) hosts);
|
||||
|
||||
colmena = {
|
||||
meta = {
|
||||
nixpkgs = import inputs.nixpkgs {inherit system;};
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ in {
|
|||
mods.server.ntfy.enable = true;
|
||||
mods.server.lemmy.enable = true;
|
||||
mods.server.audio.enable = true;
|
||||
mods.server.murmur.enable = true;
|
||||
mods.server.atuin.enable = true;
|
||||
mods.server.seedbox.enable = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ in {
|
|||
mods.xdg.enable = true;
|
||||
mods.social.enable = false;
|
||||
mods.i3.enable = false;
|
||||
mods.terminal.wezterm.enable = true;
|
||||
mods.terminal.nushell.enable = true;
|
||||
mods.terminal.zsh.enable = true;
|
||||
mods.terminal.emulator.enable = false;
|
||||
mods.terminal.development.enable = true;
|
||||
|
|
|
|||
|
|
@ -24,8 +24,16 @@ in {
|
|||
gnumeric
|
||||
opensnitch
|
||||
opensnitch-ui
|
||||
mumble
|
||||
];
|
||||
|
||||
nixpkgs.config.permittedInsecurePackages = [
|
||||
"libsoup-2.74.3"
|
||||
];
|
||||
|
||||
users.users.muon.extraGroups = ["docker"];
|
||||
virtualisation.docker.enable = true;
|
||||
|
||||
# System
|
||||
mods.user.name = "muon";
|
||||
networking.hostName = cfg.user.name;
|
||||
|
|
@ -61,6 +69,20 @@ in {
|
|||
services.xserver.windowManager.i3.enable = true;
|
||||
services.actual.enable = true;
|
||||
|
||||
# Reverse proxy: *.word.local -> localhost:3030
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
recommendedProxySettings = true;
|
||||
virtualHosts."~^(?<subdomain>.+)\\.word\\.local$" = {
|
||||
serverName = "~^(?<subdomain>.+)\\.word\\.local$";
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:3030";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
|
||||
virtualisation.virtualbox.host.enable = true;
|
||||
users.extraGroups.vboxusers.members = ["user-with-access-to-virtualbox"];
|
||||
|
||||
|
|
|
|||
|
|
@ -10,15 +10,21 @@ in {
|
|||
mods.xdg.enable = true;
|
||||
mods.social.enable = true;
|
||||
mods.i3.enable = true;
|
||||
mods.terminal.wezterm.enable = true;
|
||||
mods.terminal.nushell.enable = true;
|
||||
mods.terminal.zsh.enable = true;
|
||||
mods.terminal.emulator.enable = true;
|
||||
mods.terminal.development.enable = true;
|
||||
mods.terminal.tools.enable = true;
|
||||
mods.terminal.hr.enable = true;
|
||||
mods.terminal.gh.enable = true;
|
||||
mods.desktop.development.enable = true;
|
||||
mods.desktop.productivity.enable = true;
|
||||
mods.desktop.media.enable = true;
|
||||
mods.zen.enable = true;
|
||||
mods.obsidian.enable = true;
|
||||
mods.opencode.enable = true;
|
||||
mods.octo.enable = true;
|
||||
|
||||
# Hardware preferences
|
||||
|
||||
|
|
@ -64,6 +70,16 @@ in {
|
|||
input.sensitivity = -0.4;
|
||||
};
|
||||
|
||||
# SSH: auto-add work key to gpg-agent on first use
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
matchBlocks."*" = {
|
||||
identityFile = "~/.ssh/work_ed25519";
|
||||
addKeysToAgent = "yes";
|
||||
};
|
||||
matchBlocks."muho".identityFile = "~/.ssh/id_ed25519";
|
||||
};
|
||||
|
||||
# Version of first install
|
||||
home.stateVersion = "23.05";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,15 @@ in {
|
|||
mods.terminal.emulator.enable = true;
|
||||
mods.terminal.development.enable = true;
|
||||
mods.terminal.tools.enable = true;
|
||||
mods.terminal.gh.enable = true;
|
||||
mods.terminal.hr.enable = true;
|
||||
mods.desktop.development.enable = true;
|
||||
mods.desktop.productivity.enable = false;
|
||||
mods.zen.enable = true;
|
||||
mods.obsidian.enable = true;
|
||||
mods.theme.slideshow.enable = true;
|
||||
mods.octo.enable = true;
|
||||
mods.theme.slideshow = true;
|
||||
|
||||
home.packages = with pkgs;
|
||||
[
|
||||
|
|
@ -43,7 +47,6 @@ in {
|
|||
go
|
||||
rainfrog
|
||||
tealdeer
|
||||
gh
|
||||
(callPackage ./packages/mender-cli.nix {})
|
||||
]
|
||||
# Non-free </3
|
||||
|
|
|
|||
117
hosts/musk/configuration.nix
Normal file
117
hosts/musk/configuration.nix
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
system,
|
||||
sources,
|
||||
modulesPath,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods;
|
||||
keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKEio+Y5wBVD1wILaH2R3wV10FvVjiqy/4gGBWHOITTB muon@muon"
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKevYmkH7xvYoquBjnYZ7PJiVqf+GOh9fxAJBN6wZGBB gin4@hi.is"
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILmAOd9VbhyJeibt6Vrb101MNTk5W8+rh94Djv/C+pyu muon@muho"
|
||||
];
|
||||
in {
|
||||
# Hardware
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
"${
|
||||
builtins.fetchTarball {
|
||||
url = "https://github.com/nix-community/disko/archive/refs/tags/v1.12.0.tar.gz";
|
||||
sha256 = "0wbx518d2x54yn4xh98cgm65wvj0gpy6nia6ra7ns4j63hx14fkq";
|
||||
}
|
||||
}/module.nix"
|
||||
./disk-config.nix
|
||||
# (inputs.nixpkgs
|
||||
# + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix")
|
||||
];
|
||||
|
||||
environment.systemPackages = with inputs.nix-alien.packages.${system}; [
|
||||
nix-alien
|
||||
pkgs.libratbag
|
||||
pkgs.piper
|
||||
pkgs.libpq
|
||||
pkgs.qmk
|
||||
pkgs.jq
|
||||
pkgs.wireguard-tools
|
||||
pkgs.opencode
|
||||
];
|
||||
|
||||
boot.binfmt = {
|
||||
emulatedSystems = ["aarch64-linux"];
|
||||
preferStaticEmulators = true; # Make it work with Docker
|
||||
};
|
||||
|
||||
# System
|
||||
mods.user.name = "muon";
|
||||
networking.hostName = "musk";
|
||||
networking.hostId = "a2309091";
|
||||
mods.home.file = ./home.nix;
|
||||
nix.settings.trusted-users = ["root" "muon"];
|
||||
users.users.muon.extraGroups = ["docker"];
|
||||
|
||||
# Modules
|
||||
mods.desktop.enable = true;
|
||||
mods.boot.enable = true;
|
||||
|
||||
mods.theme.enable = true;
|
||||
mods.theme.scheme = "catppuccin-macchiato";
|
||||
mods.theme.wallpaper = ./wallpaper.png;
|
||||
|
||||
services.xserver.windowManager.i3.enable = true;
|
||||
# mods.desktop.wayland.enable = true;
|
||||
|
||||
mods.impermanence.enable = true;
|
||||
|
||||
virtualisation.docker.enable = true;
|
||||
|
||||
users.users.muon.openssh.authorizedKeys.keys = keys;
|
||||
users.users.root.openssh.authorizedKeys.keys = keys;
|
||||
|
||||
# Persist
|
||||
environment.persistence."/persist" = {
|
||||
directories = ["/etc/NetworkManager" "/var/lib/NetworkManager"];
|
||||
};
|
||||
|
||||
# Hardware preferences
|
||||
# environment.variables = {
|
||||
# WINIT_HIDPI_FACTOR = "1";
|
||||
# WINIT_X11_SCALE_FACTOR = "1";
|
||||
# };
|
||||
|
||||
## Monitors
|
||||
mods.monitors = {
|
||||
primary = {
|
||||
name = "DP-1";
|
||||
config = {
|
||||
enable = true;
|
||||
mode = "2560x1440";
|
||||
position = "0x0";
|
||||
rate = "60.00";
|
||||
dpi = 72;
|
||||
};
|
||||
};
|
||||
right = {
|
||||
name = "HDMI-1";
|
||||
config = {
|
||||
enable = true;
|
||||
mode = "2560x1440";
|
||||
position = "2560x0";
|
||||
rate = "60.00";
|
||||
dpi = 72;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
## Mouse
|
||||
services.libinput.mouse.accelProfile = "flat";
|
||||
|
||||
## Keyboard
|
||||
hardware.keyboard.qmk.enable = true;
|
||||
|
||||
# Version of first install
|
||||
system.stateVersion = "23.05";
|
||||
}
|
||||
72
hosts/musk/disk-config.nix
Normal file
72
hosts/musk/disk-config.nix
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
disko.devices = {
|
||||
disk = {
|
||||
main = {
|
||||
type = "disk";
|
||||
device = "/dev/sda";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
ESP = {
|
||||
size = "512M";
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
};
|
||||
};
|
||||
luks = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "luks";
|
||||
name = "crypted";
|
||||
# disable settings.keyFile if you want to use interactive password entry
|
||||
#passwordFile = "/tmp/secret.key"; # Interactive
|
||||
# settings = {
|
||||
# allowDiscards = true;
|
||||
# keyFile = "/tmp/secret.key";
|
||||
# };
|
||||
# additionalKeyFiles = [ "/tmp/additionalSecret.key" ];
|
||||
content = {
|
||||
type = "btrfs";
|
||||
extraArgs = [ "-f" ];
|
||||
subvolumes = {
|
||||
"/root" = {
|
||||
mountpoint = "/";
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
};
|
||||
"/home" = {
|
||||
mountpoint = "/home";
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
};
|
||||
"/nix" = {
|
||||
mountpoint = "/nix";
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
};
|
||||
"/persist" = {
|
||||
mountpoint = "/persist";
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
};
|
||||
"/log" = {
|
||||
mountpoint = "/var/log";
|
||||
mountOptions = [ "compress=zstd" "noatime" ];
|
||||
};
|
||||
"/swap" = {
|
||||
mountpoint = "/swap";
|
||||
swap.swapfile.size = "4G";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems."/persist".neededForBoot = true;
|
||||
fileSystems."/var/log".neededForBoot = true;
|
||||
}
|
||||
18
hosts/musk/hardware-configuration.nix
Normal file
18
hosts/musk/hardware-configuration.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||
# and may be overwritten by future invocations. Please make changes
|
||||
# to /etc/nixos/configuration.nix instead.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports =
|
||||
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-intel" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
}
|
||||
101
hosts/musk/home.nix
Normal file
101
hosts/musk/home.nix
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
osConfig,
|
||||
inputs,
|
||||
...
|
||||
}: let
|
||||
cfg = osConfig.mods;
|
||||
color = config.lib.stylix.colors.withHashtag;
|
||||
in {
|
||||
# Modules
|
||||
mods.xdg.enable = true;
|
||||
mods.i3.enable = true;
|
||||
# mods.hyprland.enable = true;
|
||||
mods.terminal.zsh.enable = true;
|
||||
mods.terminal.nushell.enable = true;
|
||||
mods.terminal.emulator.enable = true;
|
||||
mods.terminal.wezterm.enable = true;
|
||||
mods.terminal.development.enable = true;
|
||||
mods.terminal.tools.enable = true;
|
||||
mods.terminal.gh.enable = true;
|
||||
mods.terminal.hr.enable = true;
|
||||
mods.desktop.development.enable = true;
|
||||
mods.desktop.productivity.enable = false;
|
||||
mods.zen.enable = true;
|
||||
mods.octo.enable = true;
|
||||
|
||||
home.packages = with pkgs;
|
||||
[
|
||||
thunderbird
|
||||
pulseaudio
|
||||
pavucontrol
|
||||
alsa-utils
|
||||
rustdesk-flutter
|
||||
|
||||
# tools
|
||||
docker
|
||||
fish
|
||||
devenv
|
||||
dbeaver-bin
|
||||
ruff
|
||||
just
|
||||
go
|
||||
rainfrog
|
||||
tealdeer
|
||||
(callPackage ./packages/mender-cli.nix {})
|
||||
]
|
||||
# Non-free </3
|
||||
++ [google-cloud-sdk google-cloud-sql-proxy];
|
||||
|
||||
services.flameshot = {
|
||||
enable = true;
|
||||
settings = {
|
||||
General = {
|
||||
disabledTrayIcon = true;
|
||||
showStartupLaunchMessage = false;
|
||||
startupLaunch = true;
|
||||
|
||||
uiColor = color.base01;
|
||||
contrastUiColor = color.base00;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Hardware preferences
|
||||
## Monitors
|
||||
xsession.windowManager.i3.config.workspaceOutputAssign = [
|
||||
{
|
||||
workspace = "1";
|
||||
output = "${cfg.monitors.primary.name}";
|
||||
}
|
||||
{
|
||||
workspace = "2";
|
||||
output = "${cfg.monitors.right.name}";
|
||||
}
|
||||
];
|
||||
services.autorandr.enable = true;
|
||||
programs.autorandr = {
|
||||
enable = true;
|
||||
hooks.postswitch = {
|
||||
"notify-i3" = "${pkgs.i3}/bin/i3-msg restart";
|
||||
"set-wallpaper" = ''
|
||||
${lib.getExe pkgs.feh} --bg-fill --nofehbg ${./wallpaper.png}
|
||||
'';
|
||||
};
|
||||
profiles.default = {
|
||||
fingerprint = {
|
||||
"${cfg.monitors.right.name}" = "00ffffffffffff0030aef465010101011e1e0103803c22782a31d5a65453a0240a5054bfcf00d1c0d100b300a9c09500818081c08100e973006aa0a034504220680055502100001a565e00a0a0a029503020350055502100001a000000fd00304b0f6e1e000a202020202020000000fc00513237712d31300a202020202001ff020329f04b10050403021f1413121101230907078301000067030c001000183c681a00000101304b00023a801871382d40582c450055502100001e662156aa51001e30468f330055502100001eab22a0a050841a303020360055502100001a7c2e90a0601a1e403020360055502100001a000000000000000000000000000026";
|
||||
"${cfg.monitors.primary.name}" = "00ffffffffffff0005e30427b11a0000321f0104a53c22783be445a554529e260d5054bfef00d1c0b30095008180814081c001010101565e00a0a0a029503020350055502100001e000000ff005141424d434841303036383333000000fc00513237563447350a2020202020000000fd00304b72721e010a2020202020200163020318f14b0103051404131f120211902309070783010000a073006aa0a029500820350055502100001a2a4480a0703827403020350055502100001a023a801871382d40582c450055502100001ef03c00d051a0355060883a0055502100001c000000000000000000000000000000000000000000000000000000000000005f";
|
||||
};
|
||||
config = {
|
||||
"${cfg.monitors.primary.name}" = cfg.monitors.primary.config;
|
||||
"${cfg.monitors.right.name}" = cfg.monitors.right.config;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Version of first install
|
||||
home.stateVersion = "23.05";
|
||||
}
|
||||
54
hosts/musk/packages/mender-cli.nix
Normal file
54
hosts/musk/packages/mender-cli.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
makeWrapper,
|
||||
installShellFiles,
|
||||
xz,
|
||||
go,
|
||||
}:
|
||||
buildGoModule rec {
|
||||
pname = "mender-cli";
|
||||
version = "1.12.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "mendersoftware";
|
||||
repo = "mender-cli";
|
||||
rev = version;
|
||||
sha256 = "sha256-Pf87wTHXcFlnYsgx7ieiIJ9PWJFPUkFJYTkKJKmMFEQ=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-MqyBa+wsbuXqtM4DL/QGBUWuEYlG8BRxIXq7O1LJUyM=";
|
||||
|
||||
nativeBuildInputs = [
|
||||
makeWrapper
|
||||
installShellFiles
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
xz
|
||||
];
|
||||
|
||||
allowGoReference = true;
|
||||
|
||||
postFixup = ''
|
||||
wrapProgram "$out/bin/mender-cli" \
|
||||
--prefix PATH : ${go}/bin
|
||||
'';
|
||||
|
||||
postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
|
||||
installShellCompletion --cmd mender-cli \
|
||||
--bash <($out/bin/mender-cli completion bash) \
|
||||
--fish <($out/bin/mender-cli completion fish) \
|
||||
--zsh <($out/bin/mender-cli completion zsh) \
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Mender CLI tool to simplify integration between the Mender server and cloud services like continuous integration (CI)/build automation";
|
||||
mainProgram = "mender-cli";
|
||||
homepage = "https://github.com/mendersoftware/mender-cli/";
|
||||
changelog = "https://github.com/mendersoftware/mender-cli/releases/tag/${version}";
|
||||
license = lib.licenses.asl20;
|
||||
};
|
||||
}
|
||||
BIN
hosts/musk/wallpaper.png
Normal file
BIN
hosts/musk/wallpaper.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 MiB |
|
|
@ -5,28 +5,91 @@
|
|||
osConfig,
|
||||
...
|
||||
}: let
|
||||
fsss = with pkgs;
|
||||
writeShellApplication {
|
||||
name = "fsss";
|
||||
runtimeInputs = [flameshot curl xsel];
|
||||
# Bootstrap script that runs as the first program inside a new wezterm window.
|
||||
# It sets up the three tabs synchronously before the shell prompt appears,
|
||||
# so by the time the window is visible the layout is already complete.
|
||||
# Receives the project path as $1.
|
||||
wezterm-zmenu-init = pkgs.writeShellApplication {
|
||||
name = "wezterm-zmenu-init";
|
||||
runtimeInputs = [pkgs.wezterm pkgs.zsh];
|
||||
text = ''
|
||||
flameshot gui -r -s > /tmp/ss.png;if [ ! -s /tmp/ss.png ]; then
|
||||
exit 1
|
||||
fi
|
||||
AUTH=$(cat ${config.sops.secrets.zipline-auth.path})
|
||||
curl -H "authorization: $AUTH" https://share.muon.host/api/upload -F file=@/tmp/ss.png -H "Content-Type: multipart/form-data" -H "Format: date" -H "Image-Compression-Percent: 90" -H "No-JSON: true" | tr -d '\n' | xsel -ib;
|
||||
ZPATH=$1
|
||||
WIN=$(wezterm cli list --format json 2>/dev/null \
|
||||
| tr ',' '\n' \
|
||||
| awk -v pane="$WEZTERM_PANE" '
|
||||
/window_id/ { gsub(/.*: */,""); w=int($0) }
|
||||
/pane_id/ { gsub(/.*: */,""); if (int($0)==pane) { print w; exit } }')
|
||||
P1=$(wezterm cli spawn --window-id "$WIN" --cwd "$ZPATH" -- direnv exec . nvim)
|
||||
wezterm cli spawn --window-id "$WIN" --cwd "$ZPATH" > /dev/null
|
||||
wezterm cli spawn --window-id "$WIN" --cwd "$ZPATH" -- lazygit > /dev/null
|
||||
wezterm cli activate-tab --tab-index 0 --pane-id "$P1" 2>/dev/null
|
||||
'';
|
||||
};
|
||||
|
||||
zmenu = with pkgs;
|
||||
writeShellApplication {
|
||||
name = "zmenu";
|
||||
runtimeInputs = [zellij zoxide wmctrl i3 rofi alacritty zsh];
|
||||
text = ''
|
||||
runtimeInputs =
|
||||
[zoxide wmctrl i3 rofi zsh]
|
||||
++ lib.optionals config.mods.terminal.wezterm.enable [wezterm]
|
||||
++ lib.optionals (!config.mods.terminal.wezterm.enable) [zellij alacritty];
|
||||
text =
|
||||
if config.mods.terminal.wezterm.enable
|
||||
then ''
|
||||
ZPATH=$(zoxide query -l | sed -e "s|$HOME/||g" | rofi -dmenu)
|
||||
[[ -z "$ZPATH" ]] && exit
|
||||
ZSESH=$(echo "$ZPATH" | tr / -)
|
||||
|
||||
# Per-workspace socket registry so we can focus existing workspaces.
|
||||
SOCKDIR="$XDG_RUNTIME_DIR/zmenu-wez"
|
||||
mkdir -p "$SOCKDIR"
|
||||
SOCKFILE="$SOCKDIR/$ZSESH"
|
||||
|
||||
SOCK=""
|
||||
if [[ -f "$SOCKFILE" ]]; then
|
||||
SOCK=$(cat "$SOCKFILE")
|
||||
# Extract the PID from the socket path (gui-sock-<pid>) and check
|
||||
# if the process is still alive. Dead sockets linger on disk.
|
||||
SOCK_PID=''${SOCK##*-}
|
||||
if ! kill -0 "$SOCK_PID" 2>/dev/null; then
|
||||
rm -f "$SOCKFILE"
|
||||
SOCK=""
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$SOCK" ]]; then
|
||||
# Workspace open: raise its window by matching the wezterm PID in
|
||||
# wmctrl's process list, then activate a pane to ensure focus.
|
||||
SOCK_PID=''${SOCK##*-}
|
||||
WIN_HEX=$(wmctrl -lp | awk -v pid="$SOCK_PID" '$3==pid {print $1; exit}')
|
||||
wmctrl -i -a "$WIN_HEX"
|
||||
PANE=$(WEZTERM_UNIX_SOCKET="$SOCK" wezterm cli list --format json 2>/dev/null \
|
||||
| tr ',' '\n' \
|
||||
| awk '/pane_id/{gsub(/.*: */,""); print int($0); exit}')
|
||||
WEZTERM_UNIX_SOCKET="$SOCK" wezterm cli activate-pane --pane-id "$PANE" 2>/dev/null
|
||||
else
|
||||
# Not open: the init script runs as the first program inside the new
|
||||
# window. It sets up all tabs synchronously before the shell prompt
|
||||
# appears, so the layout is complete before anything is visible.
|
||||
wezterm start --always-new-process \
|
||||
--workspace "$ZSESH" --cwd "$HOME/$ZPATH" \
|
||||
-- ${lib.getExe wezterm-zmenu-init} "$HOME/$ZPATH" &
|
||||
WEZ_PID=$!
|
||||
|
||||
# Record the socket for future focus calls.
|
||||
for _ in $(seq 50); do
|
||||
sleep 0.1
|
||||
[[ -S /run/user/$UID/wezterm/gui-sock-$WEZ_PID ]] && break
|
||||
done
|
||||
echo "/run/user/$UID/wezterm/gui-sock-$WEZ_PID" > "$SOCKFILE"
|
||||
fi
|
||||
''
|
||||
else ''
|
||||
ZPATH=$(zoxide query -l | sed -e "s|$HOME/||g" | rofi -dmenu)
|
||||
[[ -z "$ZPATH" ]] && exit
|
||||
ZSESH=$(echo "$ZPATH" | tr / -)
|
||||
ZWIND=$(wmctrl -l | grep "$ZSESH$" || echo "")
|
||||
cd "$ZPATH"
|
||||
cd "$HOME/$ZPATH"
|
||||
if [[ -z "$ZWIND" ]]; then
|
||||
alacritty -T "$ZSESH" -e zsh -c "zellij -s $ZSESH -n dev || zellij a $ZSESH"
|
||||
else
|
||||
|
|
@ -106,7 +169,10 @@ in
|
|||
enable = true;
|
||||
config = {
|
||||
modifier = modifier;
|
||||
terminal = "alacritty";
|
||||
terminal =
|
||||
if config.mods.terminal.wezterm.enable
|
||||
then "wezterm"
|
||||
else "alacritty";
|
||||
menu = "rofi -show drun";
|
||||
|
||||
window = {
|
||||
|
|
@ -155,8 +221,10 @@ in
|
|||
// {
|
||||
"XF86AudioRaiseVolume" = "exec --no-startup-id pactl set-sink-volume 0 +2%";
|
||||
"XF86AudioLowerVolume" = "exec --no-startup-id pactl set-sink-volume 0 -2%";
|
||||
"Print" = "exec ${getExe fsss}";
|
||||
"Print" = "exec flameshot gui -c -s";
|
||||
"${modifier}+z" = "exec ${getExe zmenu}";
|
||||
"${modifier}+p" = "exec clipmenu";
|
||||
"${modifier}+b" = "exec ${getExe pkgs.rofi-rbw-x11}";
|
||||
"${modifier}+y" = "sticky toggle";
|
||||
"${modifier}+g" = "floating toggle";
|
||||
});
|
||||
|
|
|
|||
54
modules/home/desktop/packages/librediscord.nix
Normal file
54
modules/home/desktop/packages/librediscord.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
fetchFromGitLab,
|
||||
gtk3,
|
||||
libsoup_2_4,
|
||||
json-glib,
|
||||
leveldb,
|
||||
rtaudio,
|
||||
libopus,
|
||||
libsodium,
|
||||
libsecret,
|
||||
pkg-config,
|
||||
stdenv,
|
||||
lib,
|
||||
}: let
|
||||
version = "0.1.0";
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit version;
|
||||
|
||||
pname = "LibreDiscord";
|
||||
|
||||
src = fetchFromGitLab {
|
||||
owner = "Zipdox";
|
||||
repo = "LibreDiscord";
|
||||
rev = "a8130fab059e69437f30e320374fe6f5d21398f8";
|
||||
hash = "sha256-yr2pxW0e2ruMnDzkQMv2BQrOcN18m8zdzovnD4Dxr3M=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
gtk3
|
||||
libsoup_2_4
|
||||
json-glib
|
||||
leveldb
|
||||
rtaudio
|
||||
libopus
|
||||
libsodium
|
||||
libsecret
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp build/librediscord $out/bin
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Voice client for Discord written in C using GTK3 and GLib";
|
||||
homepage = "https://gitlab.com/Zipdox/LibreDiscord";
|
||||
license = lib.licenses.gpl3Only;
|
||||
};
|
||||
}
|
||||
|
|
@ -48,12 +48,12 @@ in
|
|||
programs.zsh.sessionVariables.BROWSER = "librewolf";
|
||||
|
||||
services.flameshot = {
|
||||
enable = false;
|
||||
enable = true;
|
||||
settings = {
|
||||
General = {
|
||||
disabledTrayIcon = true;
|
||||
showStartupLaunchMessage = false;
|
||||
startupLaunch = false;
|
||||
startupLaunch = true;
|
||||
|
||||
uiColor = color.base01;
|
||||
contrastUiColor = color.base00;
|
||||
|
|
|
|||
|
|
@ -17,10 +17,19 @@ in {
|
|||
# Communication
|
||||
# kotatogram-desktop
|
||||
signal-desktop
|
||||
abaddon
|
||||
vesktop-nogain
|
||||
# (callPackage ./packages/librediscord.nix {})
|
||||
jami
|
||||
|
||||
# Video
|
||||
freetube
|
||||
|
||||
# Security
|
||||
gcr
|
||||
];
|
||||
|
||||
services.gnome-keyring.enable = true;
|
||||
services.dunst.enable = true;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,5 +17,7 @@ in
|
|||
secrets.atuin-auth = {};
|
||||
secrets.hr-password = {};
|
||||
secrets.sops-key = {};
|
||||
secrets.google-db-test = {};
|
||||
secrets.google-db-prod = {};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ zipline-auth: ENC[AES256_GCM,data:RkJI6GuH7RzdcSlKn32gMGojjB6rkdDcnNUvsi/BTfJk2s
|
|||
atuin-auth: ENC[AES256_GCM,data:LDkiXWIwxor8Ro383gonJCyqu+nyxS7DrI2J8uo4Cqu2X61rBUlnpNR6YirUZS/lYAnWYJhZM7sR0G7ZNh9EgQ==,iv:UEs2KW8ImMnaQrSLrIGbVXEq86QiVPAPNIXBZpa3jFI=,tag:N0rhnPbasFzkoI3CJ9CV+Q==,type:str]
|
||||
hr-password: ENC[AES256_GCM,data:QZuzAnTJ2KgPnffHvdCWrJEM5d/FXxhX3dA1,iv:FgDw6aXDY0jCpJiYc9WOobR96TXNtnvN7neJu8drxMM=,tag:YT82wryVy3V+41w0YbMOrA==,type:str]
|
||||
sops-key: ENC[AES256_GCM,data:msX0EJqJauteOBICUsLcVgqNxqGcqvD+Xi/B2EhUX2OAoyBH5oDae8XWlQCi2RdOm4NtnrSTnG8FRQXfkXO+tne0VEfYTCjeVtU=,iv:qxpvofr56Ey17xcPpju/mQgiz+0cOYED5caAHs3myXw=,tag:oDFXh0rlc0tmV2IUJ1ezBQ==,type:str]
|
||||
google-db-test: ENC[AES256_GCM,data:ZMm/BF/k+XnZZkHMDSV/fk3ds0LAOHAmag==,iv:tmfJ7ju5yAO6Oco3jXYNyqzJr7cgshyd/SkjfYnEl6U=,tag:jMD2N6TsgbRwefhJ/XYhtg==,type:str]
|
||||
google-db-prod: ENC[AES256_GCM,data:fIPL9XKk9sAmpVsQBubSVbh3DlEEKadG9g==,iv:R34zkCIUDlk5/wg8eU8RZIanGayL+nX+7ZhyVmbcQC0=,tag:lu24b28742O46fjUaw2UBA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1m97a3eptxwpdd7h5kkqe9gkmhg6rquc64qjmlsfqfhfqv8q72crqrylhgc
|
||||
|
|
@ -40,7 +42,7 @@ sops:
|
|||
a0V1N2VjUDE4Z3R5MGxMQVNmOVp0bVUK9cppJW33tKFOSvbIn/2Dga8k7/McaTpK
|
||||
m7M+83guMzNoOlpJ/WYU1BaePcM974AgjVR0WD/v+xGBvGKubKHqtw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2025-08-04T07:58:56Z"
|
||||
mac: ENC[AES256_GCM,data:aJw3KK4GMj5/Q06v1C5rdSerdO21cNxpTIJYoxmfhBKudzD7lSL6l+d47kWoB0U4J5jtbs9obWz2MH3CvyPBapjJaSFnYEXk1JuGihf8GK3QrqLAt+dmF2ZD1FBLpQELripueneyHkzT32180hpXGnppNlgOuATlIMSPosvlpVI=,iv:SpGAyTqqbpuxcLkMq7VnLQUoR6oW0ERgnyPaqVHpaN8=,tag:OSNGT8/5E+PRhoR8dIyaSA==,type:str]
|
||||
lastmodified: "2026-01-21T14:37:21Z"
|
||||
mac: ENC[AES256_GCM,data:bxr3U1Ig0qjuOcxHeOlOrXO0xtZs0vKTuXn8GE1dJGCFDjVgakbIwiW6+2WNYUbIpipCAwdecgb0jBngwt3zKGS4PMzapUXxl7RoCr5DWCh6kSD4CCUH4v8guuy0k8SMQXDO3CdbUd/5/asIPfxlvEESCQL54X2OJlt5xpE7PsU=,iv:m/lrHHFYXFKCVEOK462II8bcFvw7k4rKEuMOHHmzT/8=,tag:jgEQso2bAShLJERsOHhrKw==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.10.2
|
||||
version: 3.11.0
|
||||
|
|
|
|||
|
|
@ -6,12 +6,17 @@
|
|||
imports = [
|
||||
./shell.nix
|
||||
./emulator.nix
|
||||
./wezterm.nix
|
||||
./nushell.nix
|
||||
./development.nix
|
||||
./tools.nix
|
||||
./yazi.nix
|
||||
./hr
|
||||
./helix
|
||||
./nvim
|
||||
./zellij
|
||||
./opencode
|
||||
./gh.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf osConfig.mods.desktop.enable {
|
||||
|
|
|
|||
|
|
@ -23,9 +23,12 @@ in {
|
|||
enable = true;
|
||||
nix-direnv.enable = true;
|
||||
enableZshIntegration = lib.mkIf config.mods.terminal.zsh.enable true;
|
||||
enableNushellIntegration = lib.mkIf config.mods.terminal.nushell.enable true;
|
||||
};
|
||||
|
||||
home.sessionVariables.EDITOR = "nvim";
|
||||
programs.zsh.sessionVariables.EDITOR = "nvim";
|
||||
programs.zsh.sessionVariables = lib.mkIf config.mods.terminal.zsh.enable {
|
||||
EDITOR = "nvim";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
30
modules/home/terminal/gh.nix
Normal file
30
modules/home/terminal/gh.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.mods.terminal.gh.enable = lib.mkEnableOption "enables gh and gh-dash";
|
||||
|
||||
config = lib.mkIf config.mods.terminal.gh.enable {
|
||||
programs.gh = {
|
||||
enable = true;
|
||||
extensions = [pkgs.gh-dash];
|
||||
};
|
||||
|
||||
programs.gh-dash = {
|
||||
enable = true;
|
||||
settings = {
|
||||
keybindings = {
|
||||
prs = [
|
||||
{
|
||||
key = "C";
|
||||
command = "nvim -c 'Octo pr edit {{.PrNumber}}'";
|
||||
description = "Open PR in Octo.nvim";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
{
|
||||
name = "nix";
|
||||
auto-format = true;
|
||||
formatter.command = "${pkgs.nixfmt-classic}/bin/nixfmt";
|
||||
formatter.command = "${pkgs.nixfmt}/bin/nixfmt";
|
||||
}
|
||||
{
|
||||
name = "rust";
|
||||
|
|
|
|||
65
modules/home/terminal/hr/default.nix
Normal file
65
modules/home/terminal/hr/default.nix
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods.terminal;
|
||||
|
||||
test-port = "5436";
|
||||
prod-port = "5437";
|
||||
in {
|
||||
options.mods.terminal.hr.enable = lib.mkEnableOption "Hefring (Work Tooling)";
|
||||
|
||||
config = lib.mkIf cfg.hr.enable {
|
||||
programs.nushell = lib.mkIf cfg.nushell.enable {
|
||||
extraConfig = ''
|
||||
$env.PROJECT_ID = if ($env | get -o PROJECT_ID | is-empty) { "mk2-test" } else { $env.PROJECT_ID }
|
||||
'' + builtins.readFile ./hr.nu;
|
||||
};
|
||||
|
||||
programs.starship.settings.custom.project_id = {
|
||||
command = "if $env.PROJECT_ID =~ 'prod' { $'(ansi yellow_bold) (ansi blue_bold)($env.PROJECT_ID)(ansi reset)' } else { $'(ansi blue_bold)($env.PROJECT_ID)(ansi reset)' }";
|
||||
when = "not ($env | get -o PROJECT_ID | is-empty)";
|
||||
shell = ["nu" "--no-config-file" "-c"];
|
||||
format = "on $output ";
|
||||
};
|
||||
|
||||
programs.zsh.initContent =
|
||||
''
|
||||
export MK2_TEST_SQL_INSTANCE_USER=gijs
|
||||
export MK2_TEST_SQL_INSTANCE_PASSWORD="$(cat ${config.sops.secrets.google-db-test.path})"
|
||||
export MK2_TEST_SQL_INSTANCE_PORT=${test-port}
|
||||
export MK2_TEST_SQL_INSTANCE_HOST=localhost
|
||||
export MK2_PROD_SQL_INSTANCE_USER=gijs
|
||||
export MK2_PROD_SQL_INSTANCE_PASSWORD="$(cat ${config.sops.secrets.google-db-prod.path})"
|
||||
export MK2_PROD_SQL_INSTANCE_HOST=localhost
|
||||
export MK2_PROD_SQL_INSTANCE_PORT=${prod-port}
|
||||
''
|
||||
+ builtins.readFile ./hr.sh;
|
||||
|
||||
systemd.user.services = let
|
||||
proxy-service = name: port: {
|
||||
"google-db-proxy-${name}" = {
|
||||
Unit = {
|
||||
Description = "Google Cloud SQL Proxy (${name})";
|
||||
After = ["network.target"];
|
||||
};
|
||||
Service = {
|
||||
Type = "simple";
|
||||
Environment = [
|
||||
"GOOGLE_APPLICATION_CREDENTIALS=${config.home.homeDirectory}/.config/gcloud/application_default_credentials.json"
|
||||
];
|
||||
ExecStart = "${pkgs.google-cloud-sql-proxy}/bin/cloud-sql-proxy mk2-${name}:europe-west1:mk2-${name}-sql-instance -p ${port}";
|
||||
Restart = "always";
|
||||
};
|
||||
Install = {
|
||||
WantedBy = ["default.target"];
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
proxy-service "test" test-port
|
||||
// proxy-service "prod" prod-port;
|
||||
};
|
||||
}
|
||||
474
modules/home/terminal/hr/hr.nu
Normal file
474
modules/home/terminal/hr/hr.nu
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
# HR - Hefring work tooling for nushell
|
||||
#
|
||||
# This module provides commands for working with Hefring Cloud Run services and Cloud Functions.
|
||||
#
|
||||
# Key features:
|
||||
# - Shell-style argument passing: -key value supports dot notation for nested structures
|
||||
# Example: hr call service -items.0.type "trip" -items.0.id "123"
|
||||
#
|
||||
# - Nushell record syntax with --data flag for complex payloads:
|
||||
# Example: hr call service --data {company_id: "abc", items: [{type: "trip"}]}
|
||||
#
|
||||
# - Automatic type detection: numbers, booleans, and JSON are parsed automatically
|
||||
# Example: -count "42" → number, -active "true" → boolean
|
||||
#
|
||||
# - Pretty display: Payloads are shown as Nushell tables before sending
|
||||
|
||||
def _hr_usage [] {
|
||||
print "Usage: hr <command>"
|
||||
print "Commands:"
|
||||
print " switch Switch PROJECT_ID between mk2-test and mk2-prod"
|
||||
print " call Call a Cloud Run service route"
|
||||
print " cf Call a Cloud Function"
|
||||
print " init py Initialize a python devenv environment (git-ignored)"
|
||||
print " init go Initialize a go devenv environment (git-ignored)"
|
||||
print " init rs Initialize a rust devenv environment (git-ignored)"
|
||||
print " init cpp Initialize a C++ devenv environment (git-ignored)"
|
||||
print " freeze Freeze dependencies to requirements.txt"
|
||||
}
|
||||
|
||||
def _hr_init_devenv [] {
|
||||
if (".gitignore" | path exists) {
|
||||
cp .gitignore .gitignore.bak
|
||||
}
|
||||
|
||||
if (which devenv | is-empty) {
|
||||
error make { msg: "Error: devenv not found in path." }
|
||||
}
|
||||
|
||||
devenv init
|
||||
print "Direnv allowed"
|
||||
|
||||
if (".gitignore.bak" | path exists) {
|
||||
mv .gitignore.bak .gitignore
|
||||
} else if (".gitignore" | path exists) {
|
||||
rm .gitignore
|
||||
}
|
||||
}
|
||||
|
||||
def _hr_add_ignores [files: list<string>] {
|
||||
let git_dir = (do { git rev-parse --git-dir } | complete)
|
||||
if $git_dir.exit_code != 0 {
|
||||
print "Warning: Not a git repository. Skipping git ignore setup."
|
||||
return
|
||||
}
|
||||
|
||||
let exclude_file = (git rev-parse --git-path info/exclude)
|
||||
mkdir (($exclude_file) | path dirname)
|
||||
|
||||
for file in $files {
|
||||
let already_exists = (
|
||||
if ($exclude_file | path exists) {
|
||||
open $exclude_file | lines | any { |line| $line == $file }
|
||||
} else {
|
||||
false
|
||||
}
|
||||
)
|
||||
if not $already_exists {
|
||||
$"($file)\n" | save --append $exclude_file
|
||||
print $"Added ($file) to local git exclude \(($exclude_file)\)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def _hr_py_files [] {
|
||||
"
|
||||
{pkgs, ...}: {
|
||||
packages = [ pkgs.google-cloud-sdk pkgs.libpq ];
|
||||
|
||||
languages.python = {
|
||||
enable = true;
|
||||
venv.enable = true;
|
||||
uv = {
|
||||
enable = true;
|
||||
sync = {
|
||||
enable = true;
|
||||
allExtras = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# We use the named index \"google\" defined in uv.toml
|
||||
env.UV_INDEX_GOOGLE_USERNAME = \"oauth2accesstoken\";
|
||||
env.PROJECT_ID = \"mk2-test\";
|
||||
|
||||
enterShell = ''
|
||||
export PATH=\"$DEVENV_STATE/venv/bin:$PATH\"
|
||||
if ! gcloud auth print-access-token >/dev/null 2>&1; then
|
||||
echo \"⚠️ gcloud not authenticated. Run 'gcloud auth login' to access Google Artifact Registry.\"
|
||||
else
|
||||
export UV_INDEX_GOOGLE_PASSWORD=$(gcloud auth print-access-token)
|
||||
fi
|
||||
'';
|
||||
}
|
||||
" | save -f devenv.nix
|
||||
|
||||
"
|
||||
[[index]]
|
||||
name = \"google\"
|
||||
url = \"https://europe-west1-python.pkg.dev/mk2-prod/python-packages/simple/\"
|
||||
" | save -f uv.toml
|
||||
}
|
||||
|
||||
def _hr_rs_files [] {
|
||||
"
|
||||
{pkgs, ...}: {
|
||||
languages.rust = {
|
||||
enable = true;
|
||||
channel = \"stable\";
|
||||
};
|
||||
}
|
||||
" | save -f devenv.nix
|
||||
|
||||
"
|
||||
inputs:
|
||||
rust-overlay:
|
||||
url: github:oxalica/rust-overlay
|
||||
inputs:
|
||||
nixpkgs:
|
||||
follows: nixpkgs
|
||||
" | save -f devenv.yaml
|
||||
}
|
||||
|
||||
def _hr_cpp_files [] {
|
||||
"
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
# Use glibc-compatible static openssl to match system libs
|
||||
staticOpenSSL = pkgs.openssl.override { static = true; };
|
||||
|
||||
# Shim to satisfy CMake looking for \"ssl.a\"
|
||||
compatOpenSSL = pkgs.runCommand \"openssl-compat\" {} ''
|
||||
mkdir -p $out/lib
|
||||
ln -s ${staticOpenSSL.out}/lib/libssl.a $out/lib/ssl.a
|
||||
ln -s ${staticOpenSSL.out}/lib/libcrypto.a $out/lib/crypto.a
|
||||
'';
|
||||
in {
|
||||
packages = [
|
||||
pkgs.cmake
|
||||
pkgs.clang-tools
|
||||
pkgs.pkg-config
|
||||
pkgs.mosquitto
|
||||
|
||||
staticOpenSSL
|
||||
compatOpenSSL
|
||||
];
|
||||
# Explicitly add lib paths so linker finds -lssl AND ssl.a
|
||||
env.LIBRARY_PATH = \"${staticOpenSSL.out}/lib:${compatOpenSSL}/lib\";
|
||||
env.CPATH = \"${staticOpenSSL.dev}/include\";
|
||||
|
||||
languages.cplusplus.enable = true;
|
||||
}
|
||||
" | save -f devenv.nix
|
||||
}
|
||||
|
||||
def _hr_go_files [] {
|
||||
"
|
||||
{pkgs, ...}: {
|
||||
languages.go = {
|
||||
enable = true;
|
||||
};
|
||||
}
|
||||
" | save -f devenv.nix
|
||||
}
|
||||
|
||||
def _hr_init_base [name: string, write_files: closure, ignores: list<string>] {
|
||||
print $"Initializing ($name) devenv..."
|
||||
|
||||
# 1. Init devenv
|
||||
_hr_init_devenv
|
||||
|
||||
# 2. Write language-specific files
|
||||
do $write_files
|
||||
|
||||
# 3. Add to local git exclude
|
||||
let base_ignores = [
|
||||
".devenv*"
|
||||
".direnv"
|
||||
"devenv.nix"
|
||||
"devenv.yaml"
|
||||
"devenv.lock"
|
||||
".envrc"
|
||||
]
|
||||
_hr_add_ignores ($base_ignores ++ $ignores)
|
||||
|
||||
direnv allow
|
||||
}
|
||||
|
||||
def _hr_setpath [data: any, path: list, value: any] {
|
||||
# Recursively build nested structure, similar to jq's setpath
|
||||
if ($path | is-empty) {
|
||||
$value
|
||||
} else if ($path | length) == 1 {
|
||||
$data | upsert ($path | first) $value
|
||||
} else {
|
||||
let key = ($path | first)
|
||||
let rest = ($path | skip 1)
|
||||
let next_key = ($rest | first)
|
||||
|
||||
# Check if key exists in data and get intermediate value
|
||||
let key_type = ($key | describe)
|
||||
let data_type = ($data | describe)
|
||||
let is_list = ($data_type | str starts-with "list") or ($data_type | str starts-with "table")
|
||||
|
||||
# Determine if key exists and get intermediate structure
|
||||
let intermediate = if $key_type == "int" and $is_list {
|
||||
# Array index
|
||||
if $key < ($data | length) {
|
||||
$data | get $key
|
||||
} else {
|
||||
# Index doesn't exist, create structure based on next key
|
||||
if ($next_key | describe) == "int" { [] } else { {} }
|
||||
}
|
||||
} else if $key_type != "int" {
|
||||
# Object key
|
||||
if $key in $data {
|
||||
$data | get $key
|
||||
} else {
|
||||
# Key doesn't exist, create structure based on next key
|
||||
if ($next_key | describe) == "int" { [] } else { {} }
|
||||
}
|
||||
} else {
|
||||
# Data is not a list but key is int - create array
|
||||
if ($next_key | describe) == "int" { [] } else { {} }
|
||||
}
|
||||
|
||||
# Recursively set the rest of the path
|
||||
let updated_intermediate = (_hr_setpath $intermediate $rest $value)
|
||||
|
||||
# Update data with the new intermediate value
|
||||
$data | upsert $key $updated_intermediate
|
||||
}
|
||||
}
|
||||
|
||||
def _hr_add_json_field [json: string, key: string, value: string] {
|
||||
# Determine if value should be parsed as raw JSON or treated as a string
|
||||
let is_bool = ($value == "true" or $value == "false")
|
||||
let is_number = ($value =~ '^-?(0|[1-9][0-9]*)(\.[0-9]+)?$')
|
||||
let is_json_container = ($value =~ '^\[' or $value =~ '^\{')
|
||||
|
||||
# Parse the value into the appropriate type
|
||||
let parsed_value = if $is_bool or $is_number {
|
||||
$value | from json
|
||||
} else if $is_json_container {
|
||||
let parsed = (do { $value | from json } | complete)
|
||||
if $parsed.exit_code == 0 {
|
||||
$value | from json
|
||||
} else {
|
||||
print $"Warning: Value for '($key)' looks like JSON but is invalid. Treating as string." --stderr
|
||||
$value
|
||||
}
|
||||
} else {
|
||||
$value
|
||||
}
|
||||
|
||||
# Convert key path (e.g., "items.0.type") to a list of path segments
|
||||
# Numbers are converted to integers for array indexing
|
||||
let path_segments = ($key | split row "." | each { |segment|
|
||||
if ($segment =~ '^[0-9]+$') {
|
||||
$segment | into int
|
||||
} else {
|
||||
$segment
|
||||
}
|
||||
})
|
||||
|
||||
let data = ($json | from json)
|
||||
_hr_setpath $data $path_segments $parsed_value | to json
|
||||
}
|
||||
|
||||
def _hr_parse_flags [args: list<string>] {
|
||||
mut json_str = "{}"
|
||||
mut i = 0
|
||||
while $i < ($args | length) {
|
||||
let arg = ($args | get $i)
|
||||
if ($arg | str starts-with "-") {
|
||||
let key = ($arg | str replace --regex '^-+' '')
|
||||
let next_i = $i + 1
|
||||
if $next_i >= ($args | length) {
|
||||
error make { msg: $"Error: Missing value for option ($key)" }
|
||||
}
|
||||
let next = ($args | get $next_i)
|
||||
if ($next | str starts-with "-") {
|
||||
error make { msg: $"Error: Missing value for option ($key)" }
|
||||
}
|
||||
$json_str = (_hr_add_json_field $json_str $key $next)
|
||||
$i = $i + 2
|
||||
} else {
|
||||
error make { msg: $"Error: Unexpected argument '($arg)'" }
|
||||
}
|
||||
}
|
||||
$json_str | from json
|
||||
}
|
||||
|
||||
def _hr_call [args: list<string>] {
|
||||
if ($args | is-empty) {
|
||||
print "Usage: hr call <route-name>[/path] [OPTIONS]"
|
||||
print ""
|
||||
print "Options:"
|
||||
print " -key value Set JSON field (supports dot notation, e.g., -items.0.type \"trip\")"
|
||||
print " --data <record> Pass structured data as a Nushell record"
|
||||
return
|
||||
}
|
||||
|
||||
let route_arg = ($args | first)
|
||||
let rest = ($args | skip 1)
|
||||
|
||||
let parts = if ($route_arg | str contains "/") {
|
||||
let service = ($route_arg | split row "/" | first)
|
||||
let path = "/" + ($route_arg | split row "/" | skip 1 | str join "/")
|
||||
{ service: $service, path: $path }
|
||||
} else {
|
||||
{ service: $route_arg, path: "" }
|
||||
}
|
||||
|
||||
let project_number = if $env.PROJECT_ID == "mk2-prod" {
|
||||
"1013087376822"
|
||||
} else {
|
||||
"322048751601"
|
||||
}
|
||||
|
||||
# Check if --data flag is present
|
||||
let data_idx = ($rest | enumerate | where item == "--data" | get index.0? | default (-1))
|
||||
|
||||
let payload_record = if $data_idx >= 0 {
|
||||
# Use --data record
|
||||
if ($data_idx + 1) >= ($rest | length) {
|
||||
error make { msg: "Error: --data requires a record argument" }
|
||||
}
|
||||
let data_str = ($rest | get ($data_idx + 1))
|
||||
# Try to parse as Nushell code to get a record
|
||||
try {
|
||||
nu -c $data_str
|
||||
} catch {
|
||||
error make { msg: $"Error: --data argument must be a valid Nushell record, got: ($data_str)" }
|
||||
}
|
||||
} else {
|
||||
# Use -key value pairs
|
||||
_hr_parse_flags $rest
|
||||
}
|
||||
|
||||
let payload = ($payload_record | to json -r)
|
||||
let url = $"https://($parts.service)-($project_number).europe-west1.run.app($parts.path)"
|
||||
|
||||
print $"Calling ($url)..."
|
||||
print ($payload | from json)
|
||||
|
||||
let token = (gcloud auth print-identity-token)
|
||||
^curl -s -S -L $url -H $"Authorization: Bearer ($token)" -H "Content-Type: application/json" -d $payload
|
||||
}
|
||||
|
||||
def _hr_cf [args: list<string>] {
|
||||
if ($args | is-empty) {
|
||||
print "Usage: hr cf <function-name> [OPTIONS]"
|
||||
print ""
|
||||
print "Options:"
|
||||
print " -key value Set JSON field (supports dot notation, e.g., -items.0.type \"trip\")"
|
||||
print " --data <record> Pass structured data as a Nushell record"
|
||||
return
|
||||
}
|
||||
|
||||
let function_name = ($args | first)
|
||||
let rest = ($args | skip 1)
|
||||
|
||||
# Check if --data flag is present
|
||||
let data_idx = ($rest | enumerate | where item == "--data" | get index.0? | default (-1))
|
||||
|
||||
let payload_record = if $data_idx >= 0 {
|
||||
# Use --data record
|
||||
if ($data_idx + 1) >= ($rest | length) {
|
||||
error make { msg: "Error: --data requires a record argument" }
|
||||
}
|
||||
let data_str = ($rest | get ($data_idx + 1))
|
||||
# Try to parse as Nushell code to get a record
|
||||
try {
|
||||
nu -c $data_str
|
||||
} catch {
|
||||
error make { msg: $"Error: --data argument must be a valid Nushell record, got: ($data_str)" }
|
||||
}
|
||||
} else {
|
||||
# Use -key value pairs
|
||||
_hr_parse_flags $rest
|
||||
}
|
||||
|
||||
let payload = ($payload_record | to json -r)
|
||||
let url = $"https://europe-west1-($env.PROJECT_ID).cloudfunctions.net/($function_name)"
|
||||
|
||||
print $"Calling ($url)..."
|
||||
print ($payload | from json)
|
||||
|
||||
let token = (gcloud auth print-identity-token)
|
||||
^curl -s -S -L $url -H $"Authorization: Bearer ($token)" -H "Content-Type: application/json" -d $payload
|
||||
}
|
||||
|
||||
# HR - Hefring work tooling
|
||||
export def --env --wrapped hr [...args: string] {
|
||||
if ($args | is-empty) {
|
||||
_hr_usage
|
||||
return
|
||||
}
|
||||
|
||||
let cmd = ($args | first)
|
||||
let rest = ($args | skip 1)
|
||||
|
||||
match $cmd {
|
||||
"switch" => {
|
||||
if ($rest | is-empty) {
|
||||
if $env.PROJECT_ID == "mk2-test" {
|
||||
$env.PROJECT_ID = "mk2-prod"
|
||||
print "Switched PROJECT_ID to mk2-prod"
|
||||
} else {
|
||||
$env.PROJECT_ID = "mk2-test"
|
||||
print "Switched PROJECT_ID to mk2-test"
|
||||
}
|
||||
} else {
|
||||
match ($rest | first) {
|
||||
"test" => {
|
||||
$env.PROJECT_ID = "mk2-test"
|
||||
print "Set PROJECT_ID to mk2-test"
|
||||
}
|
||||
"prod" => {
|
||||
$env.PROJECT_ID = "mk2-prod"
|
||||
print "Set PROJECT_ID to mk2-prod"
|
||||
}
|
||||
_ => {
|
||||
print "Usage: hr switch [test|prod]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"init" => {
|
||||
if ($rest | is-empty) {
|
||||
_hr_usage
|
||||
return
|
||||
}
|
||||
match ($rest | first) {
|
||||
"py" => { _hr_init_base "Python" { _hr_py_files } ["uv.lock" "uv.toml"] }
|
||||
"rs" => { _hr_init_base "Rust" { _hr_rs_files } [] }
|
||||
"go" => { _hr_init_base "Go" { _hr_go_files } [] }
|
||||
"cpp" => {
|
||||
_hr_init_base "C++" { _hr_cpp_files } []
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j (sys cpu | length)
|
||||
cp compile_commands.json ..
|
||||
}
|
||||
_ => { _hr_usage }
|
||||
}
|
||||
}
|
||||
"freeze" => {
|
||||
let extra_index_url = "https://europe-west1-python.pkg.dev/mk2-prod/python-packages/simple/"
|
||||
uv pip install keyrings.google-artifactregistry-auth==1.1.2 keyring
|
||||
uv pip install --no-cache -e ".[test]" --extra-index-url $extra_index_url --keyring-provider subprocess
|
||||
$"--extra-index-url ($extra_index_url)\n" | save -f requirements.txt
|
||||
uv pip freeze --exclude-editable | save --append requirements.txt
|
||||
}
|
||||
"call" => {
|
||||
_hr_call $rest
|
||||
}
|
||||
"cf" => {
|
||||
_hr_cf $rest
|
||||
}
|
||||
_ => { _hr_usage }
|
||||
}
|
||||
}
|
||||
414
modules/home/terminal/hr/hr.sh
Normal file
414
modules/home/terminal/hr/hr.sh
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
# Set default PROJECT_ID if not already set
|
||||
if [[ -z "$PROJECT_ID" ]]; then
|
||||
export PROJECT_ID="mk2-test"
|
||||
fi
|
||||
|
||||
_hr_usage() {
|
||||
echo "Usage: hr <command>"
|
||||
echo "Commands:"
|
||||
echo " switch Switch PROJECT_ID between mk2-test and mk2-prod"
|
||||
echo " call Call a Cloud Run service route"
|
||||
echo " cf Call a Cloud Function"
|
||||
echo " init py Initialize a python devenv environment (git-ignored)"
|
||||
echo " init go Initialize a go devenv environment (git-ignored)"
|
||||
echo " freeze Freeze dependencies to requirements.txt"
|
||||
}
|
||||
|
||||
_hr_init_devenv() {
|
||||
if [ -f .gitignore ]; then
|
||||
cp .gitignore .gitignore.bak
|
||||
fi
|
||||
|
||||
if command -v devenv >/dev/null; then
|
||||
devenv init
|
||||
echo "Direnv allowed"
|
||||
else
|
||||
echo "Error: devenv not found in path."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -f .gitignore.bak ]; then
|
||||
mv .gitignore.bak .gitignore
|
||||
elif [ -f .gitignore ]; then
|
||||
rm .gitignore
|
||||
fi
|
||||
}
|
||||
|
||||
_hr_add_ignores() {
|
||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
EXCLUDE_FILE=$(git rev-parse --git-path info/exclude)
|
||||
mkdir -p "$(dirname "$EXCLUDE_FILE")"
|
||||
|
||||
for file in "$@"; do
|
||||
if ! grep -Fxq "$file" "$EXCLUDE_FILE" 2>/dev/null; then
|
||||
echo "$file" >>"$EXCLUDE_FILE"
|
||||
echo "Added $file to local git exclude ($EXCLUDE_FILE)"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "Warning: Not a git repository. Skipping git ignore setup."
|
||||
fi
|
||||
}
|
||||
|
||||
_hr_py_files() {
|
||||
cat <<EOF >devenv.nix
|
||||
{pkgs, ...}: {
|
||||
packages = [ pkgs.google-cloud-sdk pkgs.libpq ];
|
||||
|
||||
languages.python = {
|
||||
enable = true;
|
||||
venv.enable = true;
|
||||
uv = {
|
||||
enable = true;
|
||||
sync = {
|
||||
enable = true;
|
||||
allExtras = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# We use the named index "google" defined in uv.toml
|
||||
env.UV_INDEX_GOOGLE_USERNAME = "oauth2accesstoken";
|
||||
env.PROJECT_ID = "mk2-test";
|
||||
|
||||
enterShell = ''
|
||||
export PATH="\$DEVENV_STATE/venv/bin:\$PATH"
|
||||
if ! gcloud auth print-access-token >/dev/null 2>&1; then
|
||||
echo "⚠️ gcloud not authenticated. Run 'gcloud auth login' to access Google Artifact Registry."
|
||||
else
|
||||
export UV_INDEX_GOOGLE_PASSWORD=\$(gcloud auth print-access-token)
|
||||
fi
|
||||
'';
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF >uv.toml
|
||||
[[index]]
|
||||
name = "google"
|
||||
url = "https://europe-west1-python.pkg.dev/mk2-prod/python-packages/simple/"
|
||||
EOF
|
||||
}
|
||||
|
||||
_hr_rs_files() {
|
||||
cat <<EOF >devenv.nix
|
||||
{pkgs, ...}: {
|
||||
languages.rust = {
|
||||
enable = true;
|
||||
channel = "stable";
|
||||
};
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF >devenv.yaml
|
||||
inputs:
|
||||
rust-overlay:
|
||||
url: github:oxalica/rust-overlay
|
||||
inputs:
|
||||
nixpkgs:
|
||||
follows: nixpkgs
|
||||
EOF
|
||||
}
|
||||
|
||||
_hr_cpp_files() {
|
||||
cat <<EOF >devenv.nix
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
# Use glibc-compatible static openssl to match system libs
|
||||
staticOpenSSL = pkgs.openssl.override { static = true; };
|
||||
|
||||
# Shim to satisfy CMake looking for "ssl.a"
|
||||
compatOpenSSL = pkgs.runCommand "openssl-compat" {} ''
|
||||
mkdir -p \$out/lib
|
||||
ln -s \${staticOpenSSL.out}/lib/libssl.a \$out/lib/ssl.a
|
||||
ln -s \${staticOpenSSL.out}/lib/libcrypto.a \$out/lib/crypto.a
|
||||
'';
|
||||
in {
|
||||
packages = [
|
||||
pkgs.cmake
|
||||
pkgs.clang-tools
|
||||
pkgs.pkg-config
|
||||
pkgs.mosquitto
|
||||
|
||||
staticOpenSSL
|
||||
compatOpenSSL
|
||||
];
|
||||
# Explicitly add lib paths so linker finds -lssl AND ssl.a
|
||||
env.LIBRARY_PATH = "\${staticOpenSSL.out}/lib:\${compatOpenSSL}/lib";
|
||||
env.CPATH = "\${staticOpenSSL.dev}/include";
|
||||
|
||||
languages.cplusplus.enable = true;
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
_hr_go_files() {
|
||||
cat <<EOF >devenv.nix
|
||||
{pkgs, ...}: {
|
||||
languages.go = {
|
||||
enable = true;
|
||||
};
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
_hr_init_base() {
|
||||
local name="$1"
|
||||
local func="$2"
|
||||
shift 2
|
||||
local ignores=("$@")
|
||||
echo "Initializing $name devenv..."
|
||||
|
||||
# 1. Init devenv
|
||||
_hr_init_devenv
|
||||
|
||||
# 2. Replace devenv.nix
|
||||
"$func"
|
||||
|
||||
# 3. Add to local git exclude
|
||||
IGNORES=(
|
||||
".devenv*"
|
||||
".direnv"
|
||||
"devenv.nix"
|
||||
"devenv.yaml"
|
||||
"devenv.lock"
|
||||
".envrc"
|
||||
"${ignores[@]}"
|
||||
)
|
||||
_hr_add_ignores "${IGNORES[@]}"
|
||||
|
||||
direnv allow
|
||||
}
|
||||
|
||||
_hr_init_py() {
|
||||
IGNORES=(
|
||||
"uv.lock"
|
||||
"uv.toml"
|
||||
)
|
||||
_hr_init_base "Python" _hr_py_files "${IGNORES[@]}"
|
||||
}
|
||||
|
||||
_hr_init_rs() {
|
||||
_hr_init_base "Rust" _hr_rs_files
|
||||
}
|
||||
|
||||
_hr_init_cpp() {
|
||||
_hr_init_base "C++" _hr_cpp_files
|
||||
mkdir -p build &&
|
||||
cd build &&
|
||||
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Release .. &&
|
||||
make -j$(nproc) &&
|
||||
cp compile_commands.json ..
|
||||
}
|
||||
|
||||
_hr_init_go() {
|
||||
_hr_init_base "Go" _hr_go_files
|
||||
}
|
||||
|
||||
_hr_freeze() {
|
||||
local extra_index_url="https://europe-west1-python.pkg.dev/mk2-prod/python-packages/simple/"
|
||||
|
||||
# Install the auth plugin and keyring CLI
|
||||
uv pip install keyrings.google-artifactregistry-auth==1.1.2 keyring
|
||||
|
||||
# Install project dependencies using the subprocess keyring provider
|
||||
uv pip install --no-cache -e ".[test]" --extra-index-url "${extra_index_url}" --keyring-provider subprocess
|
||||
|
||||
# Generate requirements.txt
|
||||
echo "--extra-index-url ${extra_index_url}" >requirements.txt
|
||||
uv pip freeze --exclude-editable >>requirements.txt
|
||||
}
|
||||
|
||||
_hr_add_json_field() {
|
||||
local json="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local jq_opt="--arg"
|
||||
|
||||
# Check if explicit boolean
|
||||
if [[ "$value" == "true" || "$value" == "false" ]]; then
|
||||
jq_opt="--argjson"
|
||||
# Check if number (integer or float, no leading zeros unless just 0)
|
||||
elif [[ "$value" =~ ^-?(0|[1-9][0-9]*)(\.[0-9]+)?$ ]]; then
|
||||
jq_opt="--argjson"
|
||||
# Check if object or array
|
||||
elif [[ "$value" == "["* || "$value" == "{"* ]]; then
|
||||
if echo "$value" | jq empty >/dev/null 2>&1; then
|
||||
jq_opt="--argjson"
|
||||
else
|
||||
# Warn to stderr, but proceed as string
|
||||
echo "Warning: Value for '$key' looks like JSON but is invalid. Treating as string." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Apply the value at the path defined by the key (dot-notation supported)
|
||||
# paths like items.0.id are converted to ["items", 0, "id"]
|
||||
echo "$json" | jq --arg k "$key" $jq_opt v "$value" \
|
||||
'setpath($k | split(".") | map(if test("^[0-9]+$") then tonumber else . end); $v)'
|
||||
}
|
||||
|
||||
_hr_call() {
|
||||
local route_arg="$1"
|
||||
shift
|
||||
|
||||
if [[ -z "$route_arg" ]]; then
|
||||
echo "Usage: hr call <route-name>[/path] <options>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local service_name
|
||||
local url_path
|
||||
|
||||
if [[ "$route_arg" == */* ]]; then
|
||||
service_name="${route_arg%%/*}"
|
||||
url_path="/${route_arg#*/}"
|
||||
else
|
||||
service_name="$route_arg"
|
||||
url_path=""
|
||||
fi
|
||||
|
||||
local project_number
|
||||
if [[ "$PROJECT_ID" == "mk2-prod" ]]; then
|
||||
project_number="1013087376822"
|
||||
else
|
||||
project_number="322048751601"
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null; then
|
||||
echo "Error: jq is required but not installed."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local json_payload="{}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if [[ "$1" == -* ]]; then
|
||||
local key="${1#-}"
|
||||
if [[ -z "$2" || "$2" == -* ]]; then
|
||||
echo "Error: Missing value for option $key"
|
||||
return 1
|
||||
fi
|
||||
local value="$2"
|
||||
|
||||
json_payload=$(_hr_add_json_field "$json_payload" "$key" "$value")
|
||||
shift 2
|
||||
else
|
||||
echo "Error: Unexpected argument '$1'"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
local url="https://${service_name}-${project_number}.europe-west1.run.app${url_path}"
|
||||
|
||||
echo "Calling $url..."
|
||||
echo "$json_payload"
|
||||
|
||||
curl "$url" \
|
||||
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$json_payload"
|
||||
}
|
||||
|
||||
_hr_cf() {
|
||||
local function_name="$1"
|
||||
shift
|
||||
|
||||
if [[ -z "$function_name" ]]; then
|
||||
echo "Usage: hr cf <function-name> <options>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null; then
|
||||
echo "Error: jq is required but not installed."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local json_payload="{}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
if [[ "$1" == -* ]]; then
|
||||
local key="${1#-}"
|
||||
if [[ -z "$2" || "$2" == -* ]]; then
|
||||
echo "Error: Missing value for option $key"
|
||||
return 1
|
||||
fi
|
||||
local value="$2"
|
||||
|
||||
json_payload=$(_hr_add_json_field "$json_payload" "$key" "$value")
|
||||
shift 2
|
||||
else
|
||||
echo "Error: Unexpected argument '$1'"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
local url="https://europe-west1-${PROJECT_ID}.cloudfunctions.net/${function_name}"
|
||||
|
||||
echo "Calling $url..."
|
||||
echo "$json_payload"
|
||||
|
||||
curl "$url" \
|
||||
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$json_payload"
|
||||
}
|
||||
|
||||
hr() {
|
||||
if [[ $# -eq 0 ]]; then
|
||||
_hr_usage
|
||||
return 1
|
||||
fi
|
||||
|
||||
local command="$1"
|
||||
shift
|
||||
|
||||
if [[ "$command" == "switch" ]]; then
|
||||
if [[ -z "$1" ]]; then
|
||||
# Toggle between test and prod
|
||||
if [[ "$PROJECT_ID" == "mk2-test" ]]; then
|
||||
export PROJECT_ID="mk2-prod"
|
||||
echo "Switched PROJECT_ID to mk2-prod"
|
||||
else
|
||||
export PROJECT_ID="mk2-test"
|
||||
echo "Switched PROJECT_ID to mk2-test"
|
||||
fi
|
||||
elif [[ "$1" == "test" ]]; then
|
||||
export PROJECT_ID="mk2-test"
|
||||
echo "Set PROJECT_ID to mk2-test"
|
||||
elif [[ "$1" == "prod" ]]; then
|
||||
export PROJECT_ID="mk2-prod"
|
||||
echo "Set PROJECT_ID to mk2-prod"
|
||||
else
|
||||
echo "Usage: hr switch [test|prod]"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Run original logic in a subshell to preserve set -e behavior without affecting current shell
|
||||
(
|
||||
set -e
|
||||
# Restore arguments for processing
|
||||
set -- "$command" "$@"
|
||||
|
||||
if [ "$1" = "init" ] && [ "$2" = "py" ]; then
|
||||
_hr_init_py
|
||||
elif [ "$1" = "init" ] && [ "$2" = "rs" ]; then
|
||||
_hr_init_rs
|
||||
elif [ "$1" = "init" ] && [ "$2" = "go" ]; then
|
||||
_hr_init_go
|
||||
elif [ "$1" = "init" ] && [ "$2" = "cpp" ]; then
|
||||
_hr_init_cpp
|
||||
elif [ "$1" = "freeze" ]; then
|
||||
_hr_freeze
|
||||
elif [ "$1" = "call" ]; then
|
||||
shift
|
||||
_hr_call "$@"
|
||||
elif [ "$1" = "cf" ]; then
|
||||
shift
|
||||
_hr_cf "$@"
|
||||
else
|
||||
_hr_usage
|
||||
exit 1
|
||||
fi
|
||||
)
|
||||
}
|
||||
79
modules/home/terminal/nushell.nix
Normal file
79
modules/home/terminal/nushell.nix
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods.terminal;
|
||||
|
||||
aliases = {
|
||||
la = "ls -la";
|
||||
".." = "cd ..";
|
||||
"..." = "cd ../..";
|
||||
"...." = "cd ../../..";
|
||||
"....." = "cd ../../../..";
|
||||
"......" = "cd ../../../../..";
|
||||
};
|
||||
in {
|
||||
options.mods.terminal.nushell.enable = lib.mkEnableOption "enables nushell";
|
||||
|
||||
config = lib.mkIf cfg.nushell.enable {
|
||||
programs.nushell = {
|
||||
enable = true;
|
||||
|
||||
shellAliases = aliases;
|
||||
|
||||
# vi mode + sensible defaults via flat assignments (avoids clobbering other modules)
|
||||
settings = {
|
||||
show_banner = false;
|
||||
edit_mode = "vi";
|
||||
cursor_shape = {
|
||||
vi_insert = "line";
|
||||
vi_normal = "block";
|
||||
};
|
||||
history = {
|
||||
max_size = 2097152;
|
||||
sync_on_enter = true;
|
||||
file_format = "sqlite";
|
||||
isolation = false;
|
||||
};
|
||||
completions = {
|
||||
case_sensitive = false;
|
||||
quick = true;
|
||||
partial = true;
|
||||
algorithm = "fuzzy";
|
||||
};
|
||||
table.mode = "rounded";
|
||||
};
|
||||
|
||||
# Append the / keybinding after all integrations (including atuin) are sourced,
|
||||
# so _atuin_search_cmd is defined when this runs.
|
||||
extraConfig = lib.mkAfter ''
|
||||
$env.config = (
|
||||
$env.config | upsert keybindings (
|
||||
$env.config.keybindings | append {
|
||||
name: atuin_search_vi_normal
|
||||
modifier: none
|
||||
keycode: char_/
|
||||
mode: vi_normal
|
||||
event: { send: executehostcommand cmd: (_atuin_search_cmd) }
|
||||
}
|
||||
)
|
||||
)
|
||||
'';
|
||||
|
||||
# Carry over zsh session variables
|
||||
extraEnv = lib.optionalString config.mods.terminal.development.enable ''
|
||||
$env.EDITOR = "nvim"
|
||||
'';
|
||||
};
|
||||
|
||||
# Starship prompt (same as zsh)
|
||||
programs.starship.enable = true;
|
||||
|
||||
# direnv nushell integration
|
||||
programs.direnv = lib.mkIf config.mods.terminal.development.enable {
|
||||
enableNushellIntegration = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ in {
|
|||
imports = [
|
||||
inputs.nvf.homeManagerModules.default
|
||||
./obsidian.nix
|
||||
./octo.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf config.mods.nvim.enable {
|
||||
|
|
@ -60,6 +61,13 @@ in {
|
|||
action = "<cmd>lua vim.lsp.buf.definition()<CR>";
|
||||
desc = "Go to Definition";
|
||||
}
|
||||
{
|
||||
key = "<C-k>";
|
||||
mode = ["n"];
|
||||
action = "<cmd>FzfLua combine pickers=keymaps;commands<CR>";
|
||||
silent = true;
|
||||
desc = "FzfLua keymaps and commands";
|
||||
}
|
||||
];
|
||||
|
||||
languages = {
|
||||
|
|
@ -93,6 +101,7 @@ in {
|
|||
|
||||
statusline.lualine.enable = true;
|
||||
telescope.enable = true;
|
||||
fzf-lua.enable = true;
|
||||
autocomplete.nvim-cmp.enable = true;
|
||||
autopairs.nvim-autopairs.enable = true;
|
||||
tabline.nvimBufferline.enable = true;
|
||||
|
|
@ -103,6 +112,7 @@ in {
|
|||
clipboard = {
|
||||
enable = true;
|
||||
providers.xclip.enable = true;
|
||||
registers = "unnamed,unnamedplus";
|
||||
};
|
||||
|
||||
mini = {
|
||||
|
|
|
|||
54
modules/home/terminal/nvim/octo.nix
Normal file
54
modules/home/terminal/nvim/octo.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options.mods.octo.enable =
|
||||
lib.mkEnableOption "enables octo";
|
||||
|
||||
config = lib.mkIf config.mods.octo.enable {
|
||||
programs.nvf.settings.vim.lazy.plugins."octo.nvim" = {
|
||||
package = pkgs.vimPlugins.octo-nvim;
|
||||
setupModule = "octo";
|
||||
setupOpts = {
|
||||
picker = "telescope";
|
||||
enable_builtin = true;
|
||||
};
|
||||
cmd = ["Octo"];
|
||||
keys = [
|
||||
{
|
||||
key = "<leader>oi";
|
||||
mode = "n";
|
||||
action = "<CMD>Octo issue list<CR>";
|
||||
desc = "List GitHub Issues";
|
||||
}
|
||||
{
|
||||
key = "<leader>op";
|
||||
mode = "n";
|
||||
action = "<CMD>Octo pr list<CR>";
|
||||
desc = "List GitHub PullRequests";
|
||||
}
|
||||
{
|
||||
key = "<leader>od";
|
||||
mode = "n";
|
||||
action = "<CMD>Octo discussion list<CR>";
|
||||
desc = "List GitHub Discussions";
|
||||
}
|
||||
{
|
||||
key = "<leader>on";
|
||||
mode = "n";
|
||||
action = "<CMD>Octo notification list<CR>";
|
||||
desc = "List GitHub Notifications";
|
||||
}
|
||||
{
|
||||
key = "<leader>os";
|
||||
mode = "n";
|
||||
action = "function() require('octo.utils').create_base_search_command { include_current_repo = true } end";
|
||||
lua = true;
|
||||
desc = "Search GitHub";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
38
modules/home/terminal/opencode/dcg.nix
Normal file
38
modules/home/terminal/opencode/dcg.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
lib,
|
||||
fetchFromGitHub,
|
||||
rustPlatform,
|
||||
openssl,
|
||||
pkg-config,
|
||||
git,
|
||||
}:
|
||||
rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "dcg";
|
||||
version = "v0.4.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "Dicklesworthstone";
|
||||
repo = "destructive_command_guard";
|
||||
tag = finalAttrs.version;
|
||||
hash = "sha256-tkjHhSMoLRV56AwUa0DkoDMoEj6gUZx/ih0VTC9C+4o=";
|
||||
};
|
||||
|
||||
cargoHash = "sha256-G6cOjl5tLdjBg7A+Itnk/t6tLzoU7gKYOTYlZm3HSlA=";
|
||||
|
||||
doCheck = false;
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkg-config
|
||||
openssl
|
||||
openssl.dev
|
||||
git
|
||||
];
|
||||
PKG_CONFIG_PATH = "${openssl.dev}/lib/pkgconfig";
|
||||
|
||||
meta = {
|
||||
description = "A high-performance hook for AI coding agents that blocks destructive commands before they execute, protecting your work from accidental deletion";
|
||||
homepage = "https://github.com/Dicklesworthstone/destructive_command_guard";
|
||||
license = lib.licenses.mit;
|
||||
maintainers = [];
|
||||
};
|
||||
})
|
||||
38
modules/home/terminal/opencode/default.nix
Normal file
38
modules/home/terminal/opencode/default.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
options.mods.opencode.enable = lib.mkEnableOption "enables opencode";
|
||||
|
||||
# imports = [
|
||||
# ./package.nix
|
||||
# ./dcg.nix
|
||||
# ];
|
||||
|
||||
config = lib.mkIf config.mods.opencode.enable {
|
||||
home.packages = with pkgs; [
|
||||
(callPackage ./dcg.nix {})
|
||||
];
|
||||
|
||||
programs.opencode = {
|
||||
enable = true;
|
||||
settings = {
|
||||
"plugin" = ["opencode-gemini-auth"];
|
||||
|
||||
"permission" = {
|
||||
"bash" = {
|
||||
"*" = "ask";
|
||||
"rm *" = "deny";
|
||||
"rmdir *" = "deny";
|
||||
"unlink *" = "deny";
|
||||
"*rm *" = "ask";
|
||||
"*rmdir *" = "ask";
|
||||
"*unlink *" = "ask";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ in {
|
|||
|
||||
programs.zsh = lib.mkIf cfg.zsh.enable {
|
||||
enable = true;
|
||||
dotDir = "${config.xdg.configHome}/zsh";
|
||||
enableCompletion = true;
|
||||
autosuggestion.enable = true;
|
||||
syntaxHighlighting.enable = true;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ in
|
|||
atuin = {
|
||||
enable = true;
|
||||
enableZshIntegration = true;
|
||||
enableNushellIntegration = true;
|
||||
flags = ["--disable-up-arrow"];
|
||||
settings = {
|
||||
sync_frequency = "5m";
|
||||
|
|
|
|||
599
modules/home/terminal/wezterm.nix
Normal file
599
modules/home/terminal/wezterm.nix
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods.terminal;
|
||||
color = config.lib.stylix.colors.withHashtag;
|
||||
|
||||
# Shell to use inside wezterm
|
||||
shell =
|
||||
if cfg.nushell.enable
|
||||
then lib.getExe pkgs.nushell
|
||||
else if cfg.zsh.enable
|
||||
then lib.getExe pkgs.zsh
|
||||
else null;
|
||||
|
||||
# ── Lua codegen helpers ──────────────────────────────────────────────────────
|
||||
|
||||
luaKey = {
|
||||
key,
|
||||
mods ? null,
|
||||
action,
|
||||
indent ? " ",
|
||||
}: let
|
||||
modsStr = lib.optionalString (mods != null) ", mods = \"${mods}\"";
|
||||
in "${indent}{ key = \"${key}\"${modsStr}, action = ${action} },\n";
|
||||
|
||||
# vi directions: key, arrow alias, wezterm direction name
|
||||
viDirs = [
|
||||
{
|
||||
key = "h";
|
||||
arrow = "LeftArrow";
|
||||
dir = "Left";
|
||||
}
|
||||
{
|
||||
key = "j";
|
||||
arrow = "DownArrow";
|
||||
dir = "Down";
|
||||
}
|
||||
{
|
||||
key = "k";
|
||||
arrow = "UpArrow";
|
||||
dir = "Up";
|
||||
}
|
||||
{
|
||||
key = "l";
|
||||
arrow = "RightArrow";
|
||||
dir = "Right";
|
||||
}
|
||||
];
|
||||
|
||||
# hjkl + arrow equivalents; mkAction :: dir -> lua-action-string
|
||||
viDirKeys = {
|
||||
mods ? null,
|
||||
indent ? " ",
|
||||
mkAction,
|
||||
}:
|
||||
lib.concatMapStrings (d:
|
||||
luaKey {
|
||||
inherit mods indent;
|
||||
key = d.key;
|
||||
action = mkAction d.dir;
|
||||
}
|
||||
+ luaKey {
|
||||
inherit mods indent;
|
||||
key = d.arrow;
|
||||
action = mkAction d.dir;
|
||||
})
|
||||
viDirs;
|
||||
|
||||
# 1-9 tab-jump bindings
|
||||
tabJumpKeys = {
|
||||
mods ? null,
|
||||
indent ? " ",
|
||||
}:
|
||||
lib.concatStrings (builtins.genList (i:
|
||||
luaKey {
|
||||
inherit mods indent;
|
||||
key = toString (i + 1);
|
||||
action = "act.ActivateTab(${toString i})";
|
||||
})
|
||||
9);
|
||||
|
||||
# hjkl + arrows for AdjustPaneSize at a given step
|
||||
resizeDirKeys = {
|
||||
step,
|
||||
indent ? " ",
|
||||
}:
|
||||
lib.concatMapStrings (d:
|
||||
luaKey {
|
||||
inherit indent;
|
||||
key = d.key;
|
||||
action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";
|
||||
}
|
||||
+ luaKey {
|
||||
inherit indent;
|
||||
key = d.arrow;
|
||||
action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";
|
||||
})
|
||||
viDirs;
|
||||
|
||||
# Uppercase HJKL fine-tune resize
|
||||
resizeShiftKeys = {
|
||||
step,
|
||||
indent ? " ",
|
||||
}:
|
||||
lib.concatMapStrings (d:
|
||||
luaKey {
|
||||
inherit indent;
|
||||
key = lib.toUpper d.key;
|
||||
action = "act.AdjustPaneSize({ \"${d.dir}\", ${toString step} })";
|
||||
})
|
||||
viDirs;
|
||||
|
||||
# Wrap a lua action string so it also pops the key table (return to locked)
|
||||
andPop = action: "act.Multiple({ ${action}, act.PopKeyTable })";
|
||||
|
||||
# Standard exit bindings, given the mode's own pop key
|
||||
exitKeys = {
|
||||
selfKey,
|
||||
indent ? " ",
|
||||
}:
|
||||
lib.concatStrings [
|
||||
(luaKey {
|
||||
inherit indent;
|
||||
key = selfKey;
|
||||
action = "act.PopKeyTable";
|
||||
})
|
||||
(luaKey {
|
||||
inherit indent;
|
||||
key = "Escape";
|
||||
action = "act.PopKeyTable";
|
||||
})
|
||||
(luaKey {
|
||||
inherit indent;
|
||||
key = "Enter";
|
||||
action = "act.PopKeyTable";
|
||||
})
|
||||
(luaKey {
|
||||
inherit indent;
|
||||
key = "Space";
|
||||
mods = "ALT";
|
||||
action = "act.PopKeyTable";
|
||||
})
|
||||
];
|
||||
|
||||
# ── Status bar ───────────────────────────────────────────────────────────────
|
||||
|
||||
# Per-mode accent colour (base16) and hint string shown in the status bar
|
||||
# Hints use nerd font glyphs: separators, icons, key labels
|
||||
modes = {
|
||||
locked = {
|
||||
fg = color.base03;
|
||||
hint = "⌥: ⌥hl: ^ud: ⌥1-9: ⌥n: ⌥s: ⌥w: ⌥t: ⌥p:";
|
||||
};
|
||||
normal = {
|
||||
fg = color.base0D;
|
||||
hint = "t: x: r: c: s: esc:";
|
||||
};
|
||||
resize = {
|
||||
fg = color.base0A;
|
||||
hint = "hjkl: HJKL: esc:";
|
||||
};
|
||||
copy_mode = {
|
||||
fg = color.base0E;
|
||||
hint = "hjkl: v: V: ^v: y: /: n/p: esc:";
|
||||
};
|
||||
search_mode = {
|
||||
fg = color.base08;
|
||||
hint = "type: ^n/^p: ^r: ↵/esc:";
|
||||
};
|
||||
};
|
||||
|
||||
# Lua table literal mapping mode name -> { fg, hint }
|
||||
modeTableEntries = lib.concatStringsSep "\n " (lib.mapAttrsToList (name: m: ''
|
||||
["${name}"] = { fg = "${m.fg}", hint = "${m.hint}" },
|
||||
'')
|
||||
modes);
|
||||
in {
|
||||
options.mods.terminal.wezterm.enable = lib.mkEnableOption "enables wezterm";
|
||||
|
||||
config = lib.mkIf cfg.wezterm.enable {
|
||||
programs.wezterm = {
|
||||
enable = true;
|
||||
|
||||
extraConfig = ''
|
||||
local wezterm = require("wezterm")
|
||||
local act = wezterm.action
|
||||
|
||||
-- ─── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
-- move_focus_or_tab: mirrors Zellij's MoveFocusOrTab.
|
||||
-- Move focus in direction; if at the edge, switch to the adjacent tab.
|
||||
local function move_focus_or_tab(direction)
|
||||
return wezterm.action_callback(function(window, pane)
|
||||
local neighbour = pane:tab():get_pane_direction(direction)
|
||||
if neighbour ~= nil then
|
||||
window:perform_action(act.ActivatePaneDirection(direction), pane)
|
||||
else
|
||||
if direction == "Left" then
|
||||
window:perform_action(act.ActivateTabRelative(-1), pane)
|
||||
elseif direction == "Right" then
|
||||
window:perform_action(act.ActivateTabRelative(1), pane)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- scroll_or_passthrough: Ctrl+key scrolls at the shell prompt but passes
|
||||
-- the key through when a program has taken the alt screen (vim, less, fzf…).
|
||||
local function scroll_or_passthrough(key, pages)
|
||||
return wezterm.action_callback(function(window, pane)
|
||||
if pane:is_alt_screen_active() then
|
||||
window:perform_action(act.SendKey({ key = key, mods = "CTRL" }), pane)
|
||||
else
|
||||
window:perform_action(act.ScrollByPage(pages), pane)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- tab_is_zoomed: true if any pane in the current tab is zoomed.
|
||||
-- Used by move_vertical to decide whether to carry zoom forward.
|
||||
local function tab_is_zoomed(pane)
|
||||
for _, info in ipairs(pane:tab():panes_with_info()) do
|
||||
if info.is_zoomed then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- move_vertical: move focus Up/Down.
|
||||
-- If any pane in the tab is currently zoomed (zoom-mode is "on" for this
|
||||
-- tab), zoom the destination pane too — stacked-panel behaviour.
|
||||
-- Otherwise just move focus plainly.
|
||||
local function move_vertical(direction)
|
||||
return wezterm.action_callback(function(window, pane)
|
||||
local neighbour = pane:tab():get_pane_direction(direction)
|
||||
if neighbour == nil then return end
|
||||
if tab_is_zoomed(pane) then
|
||||
-- unzoom_on_switch_pane unzooms the current pane when we move;
|
||||
-- then SetPaneZoomState(true) zooms the newly active pane.
|
||||
window:perform_action(
|
||||
act.Multiple({
|
||||
act.ActivatePaneDirection(direction),
|
||||
act.SetPaneZoomState(true),
|
||||
}),
|
||||
pane
|
||||
)
|
||||
else
|
||||
window:perform_action(act.ActivatePaneDirection(direction), pane)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ─── Status bar ────────────────────────────────────────────────────────────
|
||||
|
||||
-- Per-mode accent colour and hint text, generated from Nix/stylix palette
|
||||
local mode_info = {
|
||||
${modeTableEntries}
|
||||
}
|
||||
|
||||
local bg_bar = "${color.base01}"
|
||||
local fg_text = "${color.base05}"
|
||||
local fg_dim = "${color.base03}"
|
||||
local bg_main = "${color.base00}"
|
||||
|
||||
-- Render hints with the key in the mode accent colour and the icon dimmed.
|
||||
-- Each hint token is "key:icon"; we colour them individually.
|
||||
local function render_hints(hint, accent)
|
||||
local cells = {}
|
||||
for token in hint:gmatch("%S+") do
|
||||
local key, icon = token:match("^(.-):(.)$")
|
||||
if key and icon then
|
||||
table.insert(cells, { Background = { Color = bg_bar } })
|
||||
table.insert(cells, { Foreground = { Color = accent } })
|
||||
table.insert(cells, { Text = key })
|
||||
table.insert(cells, { Foreground = { Color = fg_text } })
|
||||
table.insert(cells, { Text = ":" .. icon .. " " })
|
||||
else
|
||||
table.insert(cells, { Background = { Color = bg_bar } })
|
||||
table.insert(cells, { Foreground = { Color = fg_dim } })
|
||||
table.insert(cells, { Text = token .. " " })
|
||||
end
|
||||
end
|
||||
return cells
|
||||
end
|
||||
|
||||
wezterm.on("format-window-title", function(tab, _pane, _tabs, _panes, _config)
|
||||
local ok, win = pcall(function() return wezterm.mux.get_window(tab.window_id) end)
|
||||
if ok and win then
|
||||
local ws = win:get_workspace()
|
||||
if ws ~= "default" then
|
||||
return ws .. " — " .. tab.active_pane.title
|
||||
end
|
||||
end
|
||||
return tab.active_pane.title
|
||||
end)
|
||||
|
||||
-- format-tab-title: show "rank/total title" when the active tab is in
|
||||
-- stacked (zoom) mode; otherwise show plain " title ".
|
||||
-- Returning a cell table gives us explicit padding and colours;
|
||||
-- returning a plain string loses the retro tab bar's built-in spacing.
|
||||
local tab_bg_active = "${color.base0D}"
|
||||
local tab_fg_active = "${color.base00}"
|
||||
local tab_bg_inactive = "${color.base01}"
|
||||
local tab_fg_inactive = "${color.base03}"
|
||||
|
||||
wezterm.on("format-tab-title", function(tab, _tabs, panes, _config, _hover, _max_width)
|
||||
local title = (tab.tab_title and tab.tab_title ~= "") and tab.tab_title or tab.active_pane.title
|
||||
local bg = tab.is_active and tab_bg_active or tab_bg_inactive
|
||||
local fg = tab.is_active and tab_fg_active or tab_fg_inactive
|
||||
|
||||
-- Only compute stack position for the active tab.
|
||||
-- The `panes` event parameter only contains the active pane snapshot
|
||||
-- and lacks `top`, so we use wezterm.mux.get_tab():panes_with_info()
|
||||
-- which returns full PaneInformation including top/is_zoomed/is_active.
|
||||
if tab.is_active then
|
||||
local mux_tab = wezterm.mux.get_tab(tab.tab_id)
|
||||
if mux_tab then
|
||||
local infos = mux_tab:panes_with_info()
|
||||
table.sort(infos, function(a, b) return a.top < b.top end)
|
||||
|
||||
local any_zoomed = false
|
||||
local active_rank, total = 0, 0
|
||||
for _, p in ipairs(infos) do
|
||||
total = total + 1
|
||||
if p.is_active then active_rank = total end
|
||||
if p.is_zoomed then any_zoomed = true end
|
||||
end
|
||||
|
||||
if any_zoomed and total > 1 and active_rank > 0 then
|
||||
title = active_rank .. "/" .. total .. " " .. title
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
{ Background = { Color = bg } },
|
||||
{ Foreground = { Color = fg } },
|
||||
{ Text = " " .. title .. " " },
|
||||
}
|
||||
end)
|
||||
|
||||
wezterm.on("update-status", function(window, _pane)
|
||||
local kt = window:active_key_table()
|
||||
local mode = kt or "locked"
|
||||
local info = mode_info[mode] or mode_info["locked"]
|
||||
|
||||
window:set_left_status("")
|
||||
|
||||
-- Right: coloured hints then mode badge
|
||||
local cells = { { Text = " " } }
|
||||
for _, c in ipairs(render_hints(info.hint, info.fg)) do
|
||||
table.insert(cells, c)
|
||||
end
|
||||
table.insert(cells, "ResetAttributes")
|
||||
table.insert(cells, { Background = { Color = info.fg } })
|
||||
table.insert(cells, { Foreground = { Color = bg_bar } })
|
||||
table.insert(cells, { Text = "" })
|
||||
table.insert(cells, { Foreground = { Color = bg_main } })
|
||||
table.insert(cells, { Attribute = { Intensity = "Bold" } })
|
||||
table.insert(cells, { Text = " " .. mode:upper() .. " " })
|
||||
table.insert(cells, "ResetAttributes")
|
||||
window:set_right_status(wezterm.format(cells))
|
||||
end)
|
||||
|
||||
-- ─── Appearance ────────────────────────────────────────────────────────────
|
||||
|
||||
local config = wezterm.config_builder()
|
||||
|
||||
-- Stylix sets the color scheme via its wezterm integration;
|
||||
-- this is a fallback in case it is not active.
|
||||
config.color_scheme = "Synthwave (Gogh)"
|
||||
|
||||
config.font = wezterm.font("CommitMono Nerd Font")
|
||||
config.font_size = 12.0
|
||||
|
||||
config.window_padding = { left = 6, right = 6, top = 6, bottom = 6 }
|
||||
|
||||
config.default_cursor_style = "BlinkingBar"
|
||||
config.hide_tab_bar_if_only_one_tab = false
|
||||
config.use_fancy_tab_bar = false
|
||||
config.tab_max_width = 24
|
||||
|
||||
config.colors = {
|
||||
tab_bar = {
|
||||
background = "${color.base01}",
|
||||
active_tab = { bg_color = "${color.base0D}", fg_color = "${color.base00}", intensity = "Bold" },
|
||||
inactive_tab = { bg_color = "${color.base01}", fg_color = "${color.base03}" },
|
||||
inactive_tab_hover = { bg_color = "${color.base02}", fg_color = "${color.base04}" },
|
||||
new_tab = { bg_color = "${color.base01}", fg_color = "${color.base03}" },
|
||||
new_tab_hover = { bg_color = "${color.base02}", fg_color = "${color.base04}" },
|
||||
},
|
||||
}
|
||||
config.tab_bar_at_bottom = false
|
||||
|
||||
-- ─── SSH Domains ───────────────────────────────────────────────────────────
|
||||
|
||||
config.ssh_domains = {
|
||||
{
|
||||
name = "muho",
|
||||
remote_address = "muho",
|
||||
username = "muon",
|
||||
default_prog = { "nu" },
|
||||
},
|
||||
}
|
||||
|
||||
-- ─── Shell ─────────────────────────────────────────────────────────────────
|
||||
|
||||
${lib.optionalString (shell != null) ''
|
||||
config.default_prog = { "${shell}" }
|
||||
''}
|
||||
|
||||
-- ─── Scrollback ────────────────────────────────────────────────────────────
|
||||
|
||||
config.scrollback_lines = 10000
|
||||
|
||||
-- ─── Stacked panes ─────────────────────────────────────────────────────────
|
||||
-- When switching away from a zoomed pane, unzoom it automatically.
|
||||
-- Paired with stack_focus (Alt+j/k) this gives Zellij-style stacked panels.
|
||||
config.unzoom_on_switch_pane = true
|
||||
|
||||
-- ─── Key bindings ──────────────────────────────────────────────────────────
|
||||
--
|
||||
-- Default mode = "locked" (all keys pass through to the shell).
|
||||
-- Alt+Space enters "normal" mode.
|
||||
-- From normal: t→new tab x→close tab p/n→split r→resize mode
|
||||
-- Esc / Enter / Alt+Space → back to locked from any mode.
|
||||
|
||||
config.disable_default_key_bindings = true
|
||||
|
||||
config.keys = {
|
||||
-- Alt+h/l: move focus or switch tab at edge (Zellij MoveFocusOrTab)
|
||||
-- Alt+j/k: initial bindings (overridden below by stacked-focus variants)
|
||||
${viDirKeys {
|
||||
mods = "ALT";
|
||||
indent = " ";
|
||||
mkAction = d:
|
||||
if d == "Left" || d == "Right"
|
||||
then "move_focus_or_tab(\"${d}\")"
|
||||
else "act.ActivatePaneDirection(\"${d}\")";
|
||||
}}
|
||||
-- Alt+1-9: jump to tab by number
|
||||
${tabJumpKeys {
|
||||
mods = "ALT";
|
||||
indent = " ";
|
||||
}}
|
||||
-- Alt+n: new pane (split right)
|
||||
{ key = "n", mods = "ALT", action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }) },
|
||||
-- Alt+s: split below, then zoom the new pane so stacked mode starts.
|
||||
-- Two separate perform_action calls: the split runs first and focuses
|
||||
-- the new pane, then the second call zooms window:active_pane() which
|
||||
-- is now the new pane (not the original `pane` argument).
|
||||
{ key = "s", mods = "ALT", action = wezterm.action_callback(function(window, pane)
|
||||
window:perform_action(act.SplitVertical({ domain = "CurrentPaneDomain" }), pane)
|
||||
window:perform_action(act.SetPaneZoomState(true), window:active_pane())
|
||||
end)
|
||||
},
|
||||
-- Alt+j/k: move_vertical — plain focus move normally, stacked zoom
|
||||
-- when any pane in the tab is already zoomed (mirrors Alt+f state).
|
||||
{ key = "j", mods = "ALT", action = move_vertical("Down") },
|
||||
{ key = "k", mods = "ALT", action = move_vertical("Up") },
|
||||
{ key = "DownArrow", mods = "ALT", action = move_vertical("Down") },
|
||||
{ key = "UpArrow", mods = "ALT", action = move_vertical("Up") },
|
||||
-- Alt+f: toggle zoom
|
||||
{ key = "f", mods = "ALT", action = act.TogglePaneZoomState },
|
||||
-- Alt+[/]: rotate panes
|
||||
{ key = "[", mods = "ALT", action = act.RotatePanes("CounterClockwise") },
|
||||
{ key = "]", mods = "ALT", action = act.RotatePanes("Clockwise") },
|
||||
-- Alt++/-/=: font size
|
||||
{ key = "+", mods = "ALT", action = act.IncreaseFontSize },
|
||||
{ key = "-", mods = "ALT", action = act.DecreaseFontSize },
|
||||
{ key = "=", mods = "ALT", action = act.ResetFontSize },
|
||||
-- Alt+i/o: reorder tabs
|
||||
{ key = "i", mods = "ALT", action = act.MoveTabRelative(-1) },
|
||||
{ key = "o", mods = "ALT", action = act.MoveTabRelative(1) },
|
||||
-- Alt+w: close current pane; if tab was in stacked (zoom) mode, re-zoom
|
||||
-- the new active pane so zoom mode persists across close.
|
||||
-- confirm=false because the callback must run synchronously after close;
|
||||
-- a confirm dialog would make the zoom fire before the user answers.
|
||||
-- Guard: only re-zoom when there will still be panes left after close.
|
||||
{ key = "w", mods = "ALT", action = wezterm.action_callback(function(window, pane)
|
||||
local panes = pane:tab():panes()
|
||||
local was_zoomed = tab_is_zoomed(pane)
|
||||
window:perform_action(act.CloseCurrentPane({ confirm = false }), pane)
|
||||
if was_zoomed and #panes > 1 then
|
||||
window:perform_action(act.SetPaneZoomState(true), window:active_pane())
|
||||
end
|
||||
end)
|
||||
},
|
||||
-- Alt+p: command palette
|
||||
{ key = "p", mods = "ALT", action = act.ActivateCommandPalette },
|
||||
-- Alt+t: tab navigator (searchable tab list)
|
||||
{ key = "t", mods = "ALT", action = act.ShowTabNavigator },
|
||||
-- Alt+q: quit
|
||||
{ key = "q", mods = "ALT", action = act.QuitApplication },
|
||||
-- Ctrl+U/D: scroll half page when at the shell prompt; pass through
|
||||
-- to the running program when it is using the alt screen (vim, less,
|
||||
-- fzf, etc. switch to the alt screen and need the raw key).
|
||||
{ key = "u", mods = "CTRL", action = scroll_or_passthrough("u", -0.5) },
|
||||
{ key = "d", mods = "CTRL", action = scroll_or_passthrough("d", 0.5) },
|
||||
-- Copy / paste
|
||||
{ key = "c", mods = "CTRL|SHIFT", action = act.CopyTo("Clipboard") },
|
||||
{ key = "v", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") },
|
||||
-- Enter normal mode
|
||||
{ key = "Space", mods = "ALT", action = act.ActivateKeyTable({ name = "normal", one_shot = false }) },
|
||||
}
|
||||
|
||||
config.key_tables = {
|
||||
|
||||
-- ── NORMAL ─────────────────────────────────────────────────────────────
|
||||
normal = {
|
||||
{ key = "t", action = ${andPop "act.SpawnTab(\"CurrentPaneDomain\")"} },
|
||||
{ key = "x", action = ${andPop "act.CloseCurrentTab({ confirm = true })"} },
|
||||
{ key = "r", action = act.ActivateKeyTable({ name = "resize", one_shot = false }) },
|
||||
{ key = "c", action = ${andPop "act.ActivateCopyMode"} },
|
||||
{ key = "s", action = ${andPop "act.Search({ CaseSensitiveString = \"\" })"} },
|
||||
${exitKeys {selfKey = "Escape";}} },
|
||||
|
||||
-- ── RESIZE ─────────────────────────────────────────────────────────────
|
||||
resize = {
|
||||
${resizeDirKeys {step = 5;}}${resizeShiftKeys {step = 1;}}
|
||||
{ key = "+", action = act.AdjustPaneSize({ "Right", 5 }) },
|
||||
{ key = "-", action = act.AdjustPaneSize({ "Left", 5 }) },
|
||||
{ key = "=", action = act.AdjustPaneSize({ "Right", 5 }) },
|
||||
${exitKeys {selfKey = "r";}} },
|
||||
|
||||
-- ── COPY MODE ──────────────────────────────────────────────────────────
|
||||
copy_mode = {
|
||||
-- movement
|
||||
{ key = "h", action = act.CopyMode("MoveLeft") },
|
||||
{ key = "j", action = act.CopyMode("MoveDown") },
|
||||
{ key = "k", action = act.CopyMode("MoveUp") },
|
||||
{ key = "l", action = act.CopyMode("MoveRight") },
|
||||
{ key = "LeftArrow", action = act.CopyMode("MoveLeft") },
|
||||
{ key = "DownArrow", action = act.CopyMode("MoveDown") },
|
||||
{ key = "UpArrow", action = act.CopyMode("MoveUp") },
|
||||
{ key = "RightArrow", action = act.CopyMode("MoveRight") },
|
||||
{ key = "w", action = act.CopyMode("MoveForwardWord") },
|
||||
{ key = "b", action = act.CopyMode("MoveBackwardWord") },
|
||||
{ key = "e", action = act.CopyMode("MoveForwardWordEnd") },
|
||||
{ key = "0", action = act.CopyMode("MoveToStartOfLine") },
|
||||
{ key = "^", action = act.CopyMode("MoveToStartOfLineContent") },
|
||||
{ key = "$", action = act.CopyMode("MoveToEndOfLineContent") },
|
||||
{ key = "g", action = act.CopyMode("MoveToScrollbackTop") },
|
||||
{ key = "G", action = act.CopyMode("MoveToScrollbackBottom") },
|
||||
{ key = "Enter", action = act.CopyMode("MoveToStartOfNextLine") },
|
||||
{ key = "u", mods = "CTRL", action = act.CopyMode({ MoveByPage = -0.5 }) },
|
||||
{ key = "d", mods = "CTRL", action = act.CopyMode({ MoveByPage = 0.5 }) },
|
||||
{ key = "b", mods = "CTRL", action = act.CopyMode("PageUp") },
|
||||
{ key = "f", mods = "CTRL", action = act.CopyMode("PageDown") },
|
||||
{ key = "PageUp", action = act.CopyMode("PageUp") },
|
||||
{ key = "PageDown", action = act.CopyMode("PageDown") },
|
||||
-- selection
|
||||
{ key = "v", action = act.CopyMode({ SetSelectionMode = "Cell" }) },
|
||||
{ key = "V", action = act.CopyMode({ SetSelectionMode = "Line" }) },
|
||||
{ key = "v", mods = "CTRL", action = act.CopyMode({ SetSelectionMode = "Block" }) },
|
||||
{ key = "o", action = act.CopyMode("MoveToSelectionOtherEnd") },
|
||||
{ key = "O", action = act.CopyMode("MoveToSelectionOtherEndHoriz") },
|
||||
-- yank and exit
|
||||
{ key = "y", action = act.Multiple({
|
||||
act.CopyTo("ClipboardAndPrimarySelection"),
|
||||
act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }),
|
||||
})
|
||||
},
|
||||
-- search within copy mode
|
||||
{ key = "/", action = act.Search({ CaseSensitiveString = "" }) },
|
||||
{ key = "n", action = act.CopyMode("NextMatch") },
|
||||
{ key = "p", action = act.CopyMode("PriorMatch") },
|
||||
-- exit
|
||||
{ key = "q", action = act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }) },
|
||||
{ key = "Escape", action = act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }) },
|
||||
{ key = "c", mods = "CTRL", action = act.Multiple({ act.ScrollToBottom, act.CopyMode("Close") }) },
|
||||
},
|
||||
|
||||
-- ── SEARCH MODE ────────────────────────────────────────────────────────
|
||||
search_mode = {
|
||||
-- navigate matches
|
||||
{ key = "Enter", action = act.CopyMode("AcceptPattern") },
|
||||
{ key = "n", mods = "CTRL", action = act.CopyMode("NextMatch") },
|
||||
{ key = "p", mods = "CTRL", action = act.CopyMode("PriorMatch") },
|
||||
{ key = "PageUp", action = act.CopyMode("PriorMatchPage") },
|
||||
{ key = "PageDown", action = act.CopyMode("NextMatchPage") },
|
||||
{ key = "UpArrow", action = act.CopyMode("PriorMatch") },
|
||||
{ key = "DownArrow", action = act.CopyMode("NextMatch") },
|
||||
-- cycle match type (case-sensitive / insensitive / regex)
|
||||
{ key = "r", mods = "CTRL", action = act.CopyMode("CycleMatchType") },
|
||||
-- clear search input
|
||||
{ key = "u", mods = "CTRL", action = act.CopyMode("ClearPattern") },
|
||||
-- Escape: dismiss search bar, return to copy mode at current match
|
||||
{ key = "Escape", action = act.CopyMode("AcceptPattern") },
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ in with lib; {
|
|||
config = mkIf cfg.tools.enable {
|
||||
programs.yazi = {
|
||||
enable = true;
|
||||
shellWrapperName = "y";
|
||||
settings = {
|
||||
manager = {
|
||||
ratio = [ 1 4 3 ];
|
||||
|
|
|
|||
|
|
@ -100,6 +100,15 @@ in
|
|||
10.0.0.2 muon
|
||||
10.0.0.3 muho
|
||||
10.0.0.4 muop
|
||||
127.0.0.1 word.local
|
||||
'';
|
||||
|
||||
# Wildcard DNS: *.word.local -> 127.0.0.1 via NetworkManager's built-in dnsmasq
|
||||
networking.networkmanager.dns = "dnsmasq";
|
||||
# Force resolv.conf to use local dnsmasq so wildcard DNS is actually queried
|
||||
networking.resolvconf.useLocalResolver = true;
|
||||
environment.etc."NetworkManager/dnsmasq.d/word-local.conf".text = ''
|
||||
address=/.word.local/127.0.0.1
|
||||
'';
|
||||
|
||||
# gateway =
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
|
||||
environment.systemPackages = with pkgs; [
|
||||
inputs.nix-alien.packages.${system}.nix-alien
|
||||
inputs.home-manager.packages.${system}.home-manager
|
||||
colmena
|
||||
];
|
||||
programs.nix-ld.enable = true;
|
||||
|
|
|
|||
|
|
@ -22,5 +22,6 @@ in
|
|||
xdg.portal.extraPortals = [
|
||||
pkgs.xdg-desktop-portal
|
||||
];
|
||||
xdg.portal.config.common.default = "*";
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
{ pkgs, lib, ... }: {
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
imports = [
|
||||
./containers
|
||||
./gaming
|
||||
|
|
@ -25,5 +29,6 @@
|
|||
./lemmy.nix
|
||||
./audio.nix
|
||||
./atuin.nix
|
||||
./murmur.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
25
modules/nixos/server/murmur.nix
Normal file
25
modules/nixos/server/murmur.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods.server.murmur;
|
||||
in
|
||||
with lib; {
|
||||
options.mods.server = {
|
||||
murmur = {
|
||||
enable = mkEnableOption {
|
||||
default = false;
|
||||
description = "enables murmur server";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
services.murmur = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
{ pkgs, lib, config, ... }:
|
||||
let
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) types mkOption mkEnableOption;
|
||||
cfg = config.mods.server.search;
|
||||
port = config.mods.server.nginx.ports.search;
|
||||
|
|
@ -20,22 +24,23 @@ in {
|
|||
server.bind_address = "0.0.0.0";
|
||||
server.secret_key = "temporary-before-sops";
|
||||
|
||||
enabled_plugins = [ "Hostnames plugin" "Tracker URL remover" ];
|
||||
hostnames.remove = [ "(.*.)?facebook.com$" ];
|
||||
enabled_plugins = ["Hostnames plugin" "Tracker URL remover"];
|
||||
hostnames.remove = ["(.*.)?facebook.com$"];
|
||||
hostnames.replace = {
|
||||
# Self-hosted
|
||||
"(.*.)?reddit.com$" = "reddit.muon.host";
|
||||
# "(.*.)?reddit.com$" = "reddit.muon.host";
|
||||
# "(.*.)?youtube.com$" = "videos.muon.host"; # TODO not working
|
||||
|
||||
# External
|
||||
"(.*.)?youtube.com$" = "invidious.nerdvpn.de";
|
||||
"(.*.)?imdb.com$" = "libremdb.iket.me";
|
||||
"(.*.)?imgur.com$" = "rimgo.privacyredirect.com";
|
||||
"(.*.)?twitch.com$" = "safetwitch.privacyredirect.com";
|
||||
"(.*.)?wikipedia.com$" = "wikiless.privacyredirect.com";
|
||||
"(.*.)?medium.com$" = "scribe.privacyredirect.com";
|
||||
"(.*.)?stackoverflow.com$" = "anonymousoverflow.privacyredirect.com";
|
||||
"(.*.)?github.com$" = "gothub.privacyredirect.com";
|
||||
"(.*.)?reddit.com$" = "old.reddit.com";
|
||||
# "(.*.)?youtube.com$" = "invidious.nerdvpn.de";
|
||||
# "(.*.)?imdb.com$" = "libremdb.iket.me";
|
||||
# "(.*.)?imgur.com$" = "rimgo.privacyredirect.com";
|
||||
# "(.*.)?twitch.com$" = "safetwitch.privacyredirect.com";
|
||||
# "(.*.)?wikipedia.com$" = "wikiless.privacyredirect.com";
|
||||
# "(.*.)?medium.com$" = "scribe.privacyredirect.com";
|
||||
# "(.*.)?stackoverflow.com$" = "anonymousoverflow.privacyredirect.com";
|
||||
# "(.*.)?github.com$" = "gothub.privacyredirect.com";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ in {
|
|||
stylix = {
|
||||
enable = true;
|
||||
autoEnable = true;
|
||||
base16Scheme = cfg.scheme;
|
||||
# base16Scheme = cfg.scheme;
|
||||
base16Scheme = ./synthwave84.yaml;
|
||||
image = cfg.wallpaper;
|
||||
cursor = {
|
||||
name = "phinger-cursors-light";
|
||||
|
|
|
|||
24
modules/nixos/theme/synthwave84.yaml
Normal file
24
modules/nixos/theme/synthwave84.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
system: "base16"
|
||||
name: "synthwave84"
|
||||
slug: "synthwave84"
|
||||
author: "Someone"
|
||||
variant: "dark"
|
||||
palette:
|
||||
base00: "2a2139"
|
||||
base01: "241b2f"
|
||||
base02: "34294f"
|
||||
base03: "8e6fe4"
|
||||
base04: "c1c3e6"
|
||||
base05: "f5f5ff"
|
||||
base06: "ffffff"
|
||||
base07: "fffdf8"
|
||||
base08: "f92aad"
|
||||
base09: "f97e72"
|
||||
base0A: "ffe261"
|
||||
base0B: "72f1b8"
|
||||
base0C: "78dcff"
|
||||
base0D: "36f9f6"
|
||||
base0E: "ff7edb"
|
||||
base0F: "bb8977"
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
{ pkgs, lib, config, ... }:
|
||||
let cfg = config.mods.unfree.nvidia;
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
cfg = config.mods.unfree.nvidia;
|
||||
in {
|
||||
options.mods.unfree.nvidia = {
|
||||
enable = lib.mkEnableOption {
|
||||
|
|
@ -7,7 +12,7 @@ in {
|
|||
description = "enables proprietary nvidia drivers";
|
||||
};
|
||||
packages = lib.mkOption {
|
||||
default = [ "nvidia-x11" "nvidia-settings" ];
|
||||
default = ["nvidia-x11" "nvidia-settings"];
|
||||
description = "unfree packages";
|
||||
};
|
||||
driver = lib.mkOption {
|
||||
|
|
@ -23,7 +28,7 @@ in {
|
|||
# enable32Bit = true;
|
||||
# };
|
||||
|
||||
services.xserver.videoDrivers = [ "nvidia" "nvidia-dkms" ];
|
||||
services.xserver.videoDrivers = ["nvidia"];
|
||||
|
||||
hardware.nvidia = {
|
||||
modesetting.enable = true;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
{ pkgs ? import <nixpkgs> { }, ... }: {
|
||||
{pkgs ? import <nixpkgs> {}, ...}: {
|
||||
default = pkgs.mkShell {
|
||||
NIX_CONFIG =
|
||||
"extra-experimental-features = nix-command flakes ca-derivations";
|
||||
NIX_CONFIG = "extra-experimental-features = nix-command flakes ca-derivations";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
nix
|
||||
git
|
||||
|
|
|
|||
42
utils.nix
42
utils.nix
|
|
@ -1,4 +1,7 @@
|
|||
{ inputs, system, sources, ... }: {
|
||||
{ inputs, system, sources, ... }:
|
||||
let
|
||||
pkgs = import inputs.nixpkgs { inherit system; };
|
||||
in {
|
||||
mkHost = host:
|
||||
inputs.nixpkgs.lib.nixosSystem {
|
||||
specialArgs = { inherit inputs system sources; };
|
||||
|
|
@ -11,4 +14,41 @@
|
|||
inputs.impermanence.nixosModules.impermanence
|
||||
];
|
||||
};
|
||||
|
||||
# Build a standalone HM configuration for a given host NixOS config and
|
||||
# home.nix file. osConfig is injected as a specialArg so all modules that
|
||||
# reference it continue to work.
|
||||
mkHome = { hostConfig, homeFile, username ? "muon" }:
|
||||
inputs.home-manager.lib.homeManagerConfiguration {
|
||||
inherit pkgs;
|
||||
extraSpecialArgs = {
|
||||
inherit inputs system sources;
|
||||
osConfig = hostConfig.config;
|
||||
};
|
||||
modules = [
|
||||
homeFile
|
||||
inputs.self.outputs.homeManagerModules.default
|
||||
inputs.stylix.homeManagerModules.stylix
|
||||
({ osConfig, ... }: {
|
||||
home.username = username;
|
||||
home.homeDirectory = "/home/${username}";
|
||||
# Mirror the stylix settings from the NixOS config so the standalone
|
||||
# HM build has the same theme without re-declaring it.
|
||||
stylix = {
|
||||
enable = osConfig.stylix.enable;
|
||||
autoEnable = osConfig.stylix.autoEnable;
|
||||
base16Scheme = osConfig.stylix.base16Scheme;
|
||||
image = osConfig.stylix.image;
|
||||
cursor = osConfig.stylix.cursor;
|
||||
# Forward individual font options; stylix derives fonts.packages
|
||||
# internally as read-only so we must not forward that attr.
|
||||
fonts.monospace = osConfig.stylix.fonts.monospace;
|
||||
fonts.sansSerif = osConfig.stylix.fonts.sansSerif;
|
||||
fonts.serif = osConfig.stylix.fonts.serif;
|
||||
fonts.emoji = osConfig.stylix.fonts.emoji;
|
||||
fonts.sizes = osConfig.stylix.fonts.sizes;
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue