Compare commits
7 Commits
e12d425df5
...
4c29259470
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c29259470 | |||
| 7b72236b4c | |||
| 60120d46bf | |||
| b64a7416c9 | |||
| 3138f6ce11 | |||
| 6569d7d4d8 | |||
| eae286c5ab |
55
README.md
Normal file
55
README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# NixOS Config
|
||||
|
||||
Personal Nix flake for four machines:
|
||||
|
||||
- `michael` - x86_64 Linux server
|
||||
- `tahani` - x86_64 Linux home server / workstation
|
||||
- `chidi` - aarch64 Darwin work laptop
|
||||
- `jason` - aarch64 Darwin personal laptop
|
||||
|
||||
## Repository Map
|
||||
|
||||
- `modules/` - flake-parts modules, auto-imported via `import-tree`
|
||||
- `modules/_hosts/` - host-specific submodules like hardware, disks, and services
|
||||
- `modules/_lib/` - local helper functions
|
||||
- `apps/` - Nushell apps exposed through the flake
|
||||
- `secrets/` - SOPS-encrypted secrets
|
||||
- `flake.nix` - generated flake entrypoint
|
||||
- `modules/dendritic.nix` - source of truth for flake inputs and `flake.nix` generation
|
||||
|
||||
## How It Is Structured
|
||||
|
||||
This repo uses `den` and organizes configuration around aspects instead of putting everything directly in host files.
|
||||
|
||||
- shared behavior lives in `den.aspects.<name>.<class>` modules
|
||||
- hosts are declared in `modules/hosts.nix`
|
||||
- host composition happens in `modules/<host>.nix`
|
||||
- user-level config mostly lives in Home Manager aspects
|
||||
|
||||
Common examples:
|
||||
|
||||
- `modules/core.nix` - shared Nix and shell foundation
|
||||
- `modules/dev-tools.nix` - VCS, language, and developer tooling
|
||||
- `modules/network.nix` - SSH, fail2ban, and tailscale aspects
|
||||
- `modules/michael.nix` - server composition for `michael`
|
||||
- `modules/tahani.nix` - server/workstation composition for `tahani`
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
nix run .#build
|
||||
nix run .#build -- michael
|
||||
nix run .#apply
|
||||
nix run .#deploy -- .#tahani
|
||||
nix flake check
|
||||
alejandra .
|
||||
```
|
||||
|
||||
## Updating The Flake
|
||||
|
||||
`flake.nix` is generated. Update inputs in `modules/dendritic.nix`, then regenerate:
|
||||
|
||||
```bash
|
||||
nix run .#write-flake
|
||||
alejandra .
|
||||
```
|
||||
66
flake.lock
generated
66
flake.lock
generated
@@ -114,11 +114,11 @@
|
||||
},
|
||||
"den": {
|
||||
"locked": {
|
||||
"lastModified": 1773203359,
|
||||
"narHash": "sha256-CU+bk7KxCggu9MNFs6Yp4K8e24RrRYOOnqT0ZPT82+U=",
|
||||
"lastModified": 1773295801,
|
||||
"narHash": "sha256-Wzk6tz4K8lID2El0BG3F3JLFPEx6P74E3UyJwBlE+I0=",
|
||||
"owner": "vic",
|
||||
"repo": "den",
|
||||
"rev": "101b08a257a1ff892901b02f67109ec69f75466f",
|
||||
"rev": "7c781f55d26fe40d84961557aa2e1e9480cee93e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -237,11 +237,11 @@
|
||||
},
|
||||
"flake-aspects": {
|
||||
"locked": {
|
||||
"lastModified": 1773185256,
|
||||
"narHash": "sha256-e3YqMwdQ2EpVTuWRkK2rkrHywp8QCpkj7x2U53SNoIA=",
|
||||
"lastModified": 1773272797,
|
||||
"narHash": "sha256-sSdYZiIeo98LmdnCR5GMN8B8bsHFgWA+1l9ZEpwXrFU=",
|
||||
"owner": "vic",
|
||||
"repo": "flake-aspects",
|
||||
"rev": "81a51a8997abe392b9d0794424a4823adc9bd3af",
|
||||
"rev": "cc5a09d16af05210afe01c22f6b868929a4163b6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -268,11 +268,11 @@
|
||||
},
|
||||
"flake-file": {
|
||||
"locked": {
|
||||
"lastModified": 1773180483,
|
||||
"narHash": "sha256-Fz2OYAgaYzmm6KhQWMk18JuyxgVb80pLm7hlXnmXbtg=",
|
||||
"lastModified": 1773224147,
|
||||
"narHash": "sha256-w9RQyKZSTfqoZPRzIf7H4qVHy2N6uFk1MUU+c1K4c40=",
|
||||
"owner": "vic",
|
||||
"repo": "flake-file",
|
||||
"rev": "8edcfd6d941a1a936296c89fd70dd686df947be8",
|
||||
"rev": "97bd69ff570dddccd704077830446ec1ca3a6988",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -406,11 +406,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773179137,
|
||||
"narHash": "sha256-EdW2bwzlfme0vbMOcStnNmKlOAA05Bp6su2O8VLGT0k=",
|
||||
"lastModified": 1773286336,
|
||||
"narHash": "sha256-+yFtmhOHterllxWmV6YbdevTXpJdGS0mS0UmJ0k9fh0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "3f98e2bbc661ec0aaf558d8a283d6955f05f1d09",
|
||||
"rev": "7d06e0cefe6e4a1e85b2b3274dcb0b3da242a557",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -422,11 +422,11 @@
|
||||
"homebrew-cask": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1773204415,
|
||||
"narHash": "sha256-ayKgBC9e8y1sx2BHstY1OPrKMBIJAJoIQxIX7HGstGo=",
|
||||
"lastModified": 1773303437,
|
||||
"narHash": "sha256-grP7e0BRp8GDT28hAV5H4Pu51IDQ34913hxGldlQf70=",
|
||||
"owner": "homebrew",
|
||||
"repo": "homebrew-cask",
|
||||
"rev": "6b75b8e53d71974d5e81dde8efd27b2937516ac8",
|
||||
"rev": "0300017ecdeaa28c6635c559381ff4d0e1203c86",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -438,11 +438,11 @@
|
||||
"homebrew-core": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1773213089,
|
||||
"narHash": "sha256-A64kuAmOD7BLz6ESkpNayFNL+gXF6dMYuF2O8ZgGJ/k=",
|
||||
"lastModified": 1773306422,
|
||||
"narHash": "sha256-EvboT0Gno/1BZ7OcaDFZ737cQr2J6amdrbpHAFbameU=",
|
||||
"owner": "homebrew",
|
||||
"repo": "homebrew-core",
|
||||
"rev": "a8f0bec4e248fa90aa84a7c4a5b3fbc9c160b6c6",
|
||||
"rev": "f6d9718a1bee00675755ebfbe80964990bebcfda",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -526,11 +526,11 @@
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773201098,
|
||||
"narHash": "sha256-yq35qMKDHyMdVlhGfR5BojbjniY2cY9XYmiILeCf1Xc=",
|
||||
"lastModified": 1773302483,
|
||||
"narHash": "sha256-NFnShCTM8fsNF8dRMIgebo8oiTHMKyCEBieYon4K0Ic=",
|
||||
"owner": "numtide",
|
||||
"repo": "llm-agents.nix",
|
||||
"rev": "8578734bf5087a1ca45033c2ec8e1a2228f9b95c",
|
||||
"rev": "dd92bb728d845be99ebfee69c1ee951dcbceb2b1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -652,11 +652,11 @@
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1772956932,
|
||||
"narHash": "sha256-M0yS4AafhKxPPmOHGqIV0iKxgNO8bHDWdl1kOwGBwRY=",
|
||||
"lastModified": 1773110118,
|
||||
"narHash": "sha256-mPAG8phMbCReKSiKAijjjd3v7uVcJOQ75gSjGJjt/Rk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "608d0cadfed240589a7eea422407a547ad626a14",
|
||||
"rev": "e607cb5360ff1234862ac9f8839522becb853bb9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -668,11 +668,11 @@
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1773212285,
|
||||
"narHash": "sha256-56OmaHrnvxJA1D78ZDqBME9ptqRMhw3r+vw2/UKfD24=",
|
||||
"lastModified": 1773305098,
|
||||
"narHash": "sha256-fm+rOZiAzFivuco5d7T+rE8i213EIEuHvoIuWiVlYto=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "15d31785a653f47d6f8fe017ae244bb5282fa3e5",
|
||||
"rev": "b9637319b2e3b8061ba416b46cde41bd9f8b4442",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -753,11 +753,11 @@
|
||||
"nono": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1773209960,
|
||||
"narHash": "sha256-SZ6Qze+sZpP2gqMmhyYSIlMaumJUhzaSIJwKJaOP4Mo=",
|
||||
"lastModified": 1773270026,
|
||||
"narHash": "sha256-aLMBK2JQkn07wzho79WwolXRZEqvpILdq5bxp9F6Wug=",
|
||||
"owner": "always-further",
|
||||
"repo": "nono",
|
||||
"rev": "910034e23d37882862e9694f737094b0a3e67711",
|
||||
"rev": "9f3205847598d04d8b882c8ecb8238e4855d2a41",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -1022,11 +1022,11 @@
|
||||
"utils": "utils_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773180218,
|
||||
"narHash": "sha256-VEt3lAK7jMVE1mkW2S8RaCd3G19M102pCGR6ge5MBsY=",
|
||||
"lastModified": 1773260683,
|
||||
"narHash": "sha256-ZZEX8JD+kiqpNsPOgrksbNrh5w8B63s8ebJplftdqhQ=",
|
||||
"owner": "agavra",
|
||||
"repo": "tuicr",
|
||||
"rev": "544f8b6f3bbef4577f6b79ded2974684d8a43582",
|
||||
"rev": "392d5e5a7b443cb7bbf234f7f44c6c0352b99c8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -45,7 +45,7 @@ in {
|
||||
{path = "/System/Applications/Music.app/";}
|
||||
{path = "/System/Applications/System Settings.app/";}
|
||||
{
|
||||
path = "${config.users.users.cschmatzler.home}/Downloads";
|
||||
path = "/Users/cschmatzler/Downloads";
|
||||
section = "others";
|
||||
options = "--sort name --view grid --display stack";
|
||||
}
|
||||
|
||||
58
modules/_hosts/michael/backups.nix
Normal file
58
modules/_hosts/michael/backups.nix
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
services.restic.backups.gitea = {
|
||||
repository = "s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories";
|
||||
paths = ["/var/lib/gitea"];
|
||||
exclude = [
|
||||
"/var/lib/gitea/log"
|
||||
"/var/lib/gitea/data/gitea.db"
|
||||
"/var/lib/gitea/data/gitea.db-shm"
|
||||
"/var/lib/gitea/data/gitea.db-wal"
|
||||
];
|
||||
passwordFile = config.sops.secrets.michael-gitea-restic-password.path;
|
||||
environmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
||||
pruneOpts = [
|
||||
"--keep-daily 7"
|
||||
"--keep-weekly 4"
|
||||
"--keep-monthly 6"
|
||||
];
|
||||
timerConfig = {
|
||||
OnCalendar = "daily";
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = "1h";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.restic-backups-gitea = {
|
||||
wants = ["restic-init-gitea.service"];
|
||||
after = ["restic-init-gitea.service"];
|
||||
serviceConfig = {
|
||||
User = lib.mkForce "gitea";
|
||||
Group = lib.mkForce "gitea";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.restic-init-gitea = {
|
||||
description = "Initialize Restic repository for Gitea backups";
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network-online.target"];
|
||||
wants = ["network-online.target"];
|
||||
path = [pkgs.restic];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "gitea";
|
||||
Group = "gitea";
|
||||
RemainAfterExit = true;
|
||||
EnvironmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
||||
};
|
||||
script = ''
|
||||
export RESTIC_PASSWORD=$(cat ${config.sops.secrets.michael-gitea-restic-password.path})
|
||||
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories snapshots &>/dev/null || \
|
||||
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories init
|
||||
'';
|
||||
};
|
||||
}
|
||||
114
modules/_hosts/michael/gitea.nix
Normal file
114
modules/_hosts/michael/gitea.nix
Normal file
@@ -0,0 +1,114 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: {
|
||||
sops.secrets = {
|
||||
michael-gitea-litestream = {
|
||||
sopsFile = ../../../secrets/michael-gitea-litestream;
|
||||
format = "binary";
|
||||
owner = "gitea";
|
||||
group = "gitea";
|
||||
path = "/run/secrets/michael-gitea-litestream";
|
||||
};
|
||||
michael-gitea-restic-password = {
|
||||
sopsFile = ../../../secrets/michael-gitea-restic-password;
|
||||
format = "binary";
|
||||
owner = "gitea";
|
||||
group = "gitea";
|
||||
path = "/run/secrets/michael-gitea-restic-password";
|
||||
};
|
||||
michael-gitea-restic-env = {
|
||||
sopsFile = ../../../secrets/michael-gitea-restic-env;
|
||||
format = "binary";
|
||||
owner = "gitea";
|
||||
group = "gitea";
|
||||
path = "/run/secrets/michael-gitea-restic-env";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [80 443];
|
||||
|
||||
services.redis.servers.gitea = {
|
||||
enable = true;
|
||||
port = 6380;
|
||||
bind = "127.0.0.1";
|
||||
settings = {
|
||||
maxmemory = "64mb";
|
||||
maxmemory-policy = "allkeys-lru";
|
||||
};
|
||||
};
|
||||
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
path = "/var/lib/gitea/data/gitea.db";
|
||||
};
|
||||
settings = {
|
||||
server = {
|
||||
ROOT_URL = "https://git.schmatzler.com/";
|
||||
DOMAIN = "git.schmatzler.com";
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
HTTP_PORT = 3000;
|
||||
LANDING_PAGE = "explore";
|
||||
};
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
security.INSTALL_LOCK = true;
|
||||
cache = {
|
||||
ADAPTER = "redis";
|
||||
HOST = "redis://127.0.0.1:6380/0?pool_size=100&idle_timeout=180s";
|
||||
ITEM_TTL = "16h";
|
||||
};
|
||||
"cache.last_commit" = {
|
||||
ITEM_TTL = "8760h";
|
||||
COMMITS_COUNT = 100;
|
||||
};
|
||||
session = {
|
||||
PROVIDER = "redis";
|
||||
PROVIDER_CONFIG = "redis://127.0.0.1:6380/1?pool_size=100&idle_timeout=180s";
|
||||
COOKIE_SECURE = true;
|
||||
SAME_SITE = "strict";
|
||||
};
|
||||
api.ENABLE_SWAGGER = false;
|
||||
};
|
||||
};
|
||||
|
||||
services.litestream = {
|
||||
enable = true;
|
||||
environmentFile = config.sops.secrets.michael-gitea-litestream.path;
|
||||
settings = {
|
||||
dbs = [
|
||||
{
|
||||
path = "/var/lib/gitea/data/gitea.db";
|
||||
replicas = [
|
||||
{
|
||||
type = "s3";
|
||||
bucket = "michael-gitea-litestream";
|
||||
path = "gitea";
|
||||
endpoint = "s3.eu-central-003.backblazeb2.com";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.litestream.serviceConfig = {
|
||||
User = lib.mkForce "gitea";
|
||||
Group = lib.mkForce "gitea";
|
||||
};
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
virtualHosts."git.schmatzler.com".extraConfig = ''
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
}
|
||||
reverse_proxy localhost:3000
|
||||
'';
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
{config, ...}: {
|
||||
services.adguardhome = {
|
||||
enable = true;
|
||||
host = "0.0.0.0";
|
||||
host = "127.0.0.1";
|
||||
port = 10000;
|
||||
settings = {
|
||||
dhcp = {
|
||||
@@ -57,4 +57,13 @@
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services.caddy.virtualHosts."adguard.manticore-hippocampus.ts.net" = {
|
||||
extraConfig = ''
|
||||
tls {
|
||||
get_certificate tailscale
|
||||
}
|
||||
reverse_proxy localhost:${toString config.services.adguardhome.port}
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{config, ...}: {
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
enableReload = false;
|
||||
globalConfig = ''
|
||||
admin off
|
||||
'';
|
||||
@@ -25,8 +26,11 @@
|
||||
virtualisation.oci-containers = {
|
||||
backend = "docker";
|
||||
containers.paperless-ai = {
|
||||
image = "clusterzx/paperless-ai:latest";
|
||||
image = "clusterzx/paperless-ai:v3.0.9";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"127.0.0.1:3000:3000"
|
||||
];
|
||||
volumes = [
|
||||
"paperless-ai-data:/app/data"
|
||||
];
|
||||
@@ -36,11 +40,10 @@
|
||||
PAPERLESS_AI_PORT = "3000";
|
||||
# Initial setup wizard will configure the rest
|
||||
PAPERLESS_AI_INITIAL_SETUP = "yes";
|
||||
# Paperless-ngx API URL accessible from container (using host network)
|
||||
PAPERLESS_API_URL = "http://127.0.0.1:${toString config.services.paperless.port}/api";
|
||||
PAPERLESS_API_URL = "http://host.docker.internal:${toString config.services.paperless.port}/api";
|
||||
};
|
||||
extraOptions = [
|
||||
"--network=host"
|
||||
"--add-host=host.docker.internal:host-gateway"
|
||||
];
|
||||
};
|
||||
};
|
||||
@@ -57,7 +60,7 @@
|
||||
|
||||
services.paperless = {
|
||||
enable = true;
|
||||
address = "0.0.0.0";
|
||||
address = "127.0.0.1";
|
||||
passwordFile = config.sops.secrets.tahani-paperless-password.path;
|
||||
settings = {
|
||||
PAPERLESS_DBENGINE = "sqlite";
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
user = "cschmatzler";
|
||||
|
||||
sshKeys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHfRZQ+7ejD3YHbyMTrV0gN1Gc0DxtGgl5CVZSupo5ws"
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL/I+/2QT47raegzMIyhwMEPKarJP/+Ox9ewA4ZFJwk/"
|
||||
];
|
||||
|
||||
stateVersions = {
|
||||
darwin = 6;
|
||||
nixos = "25.11";
|
||||
homeManager = "25.11";
|
||||
};
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{pkgs}:
|
||||
pkgs.writeShellScriptBin "open-project" ''
|
||||
TARGET=$(fd -t d --exact-depth 1 . $HOME/Projects |
|
||||
sed "s~$HOME/Projects/~~" |
|
||||
fzf --prompt "project > ")
|
||||
|
||||
if [ -n "$TARGET" ]; then
|
||||
echo "$HOME/Projects/$TARGET"
|
||||
fi
|
||||
''
|
||||
@@ -25,10 +25,10 @@
|
||||
enable = true;
|
||||
defaultEditor = true;
|
||||
luaLoader.enable = true;
|
||||
colorschemes.catppuccin = {
|
||||
colorschemes.rose-pine = {
|
||||
enable = true;
|
||||
settings = {
|
||||
flavour = "latte";
|
||||
variant = "dawn";
|
||||
};
|
||||
};
|
||||
extraConfigLua = ''
|
||||
|
||||
@@ -19,17 +19,6 @@ in {
|
||||
];
|
||||
extraConfigLua = ''
|
||||
require('code-review').setup({
|
||||
comment = {
|
||||
storage = {
|
||||
backend = "file",
|
||||
file = {
|
||||
dir = ".code-review",
|
||||
},
|
||||
},
|
||||
},
|
||||
output = {
|
||||
format = "minimal",
|
||||
},
|
||||
keymaps = false,
|
||||
})
|
||||
'';
|
||||
|
||||
@@ -1,173 +1,351 @@
|
||||
{
|
||||
programs.nixvim.plugins.mini = {
|
||||
enable = true;
|
||||
modules = {
|
||||
ai = {
|
||||
custom_textobjects = {
|
||||
B.__raw = "require('mini.extra').gen_ai_spec.buffer()";
|
||||
F.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@function.outer', i = '@function.inner' })";
|
||||
};
|
||||
};
|
||||
align = {};
|
||||
basics = {
|
||||
options = {
|
||||
basic = true;
|
||||
extra_ui = true;
|
||||
};
|
||||
mappings = {
|
||||
basic = false;
|
||||
};
|
||||
autocommands = {
|
||||
basic = true;
|
||||
};
|
||||
};
|
||||
bracketed = {};
|
||||
clue = {
|
||||
clues.__raw = ''
|
||||
{
|
||||
{ mode = 'n', keys = '<Leader>e', desc = '+Explore/+Edit' },
|
||||
{ mode = 'n', keys = '<Leader>f', desc = '+Find' },
|
||||
{ mode = 'n', keys = '<Leader>v', desc = '+VCS' },
|
||||
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
||||
{ mode = 'x', keys = '<Leader>l', desc = '+LSP' },
|
||||
{ mode = 'n', keys = '<Leader>o', desc = '+OpenCode' },
|
||||
{ mode = 'x', keys = '<Leader>o', desc = '+OpenCode' },
|
||||
{ mode = 'n', keys = '<Leader>r', desc = '+Review' },
|
||||
{ mode = 'v', keys = '<Leader>r', desc = '+Review' },
|
||||
require("mini.clue").gen_clues.builtin_completion(),
|
||||
require("mini.clue").gen_clues.g(),
|
||||
require("mini.clue").gen_clues.marks(),
|
||||
require("mini.clue").gen_clues.registers(),
|
||||
require("mini.clue").gen_clues.windows({ submode_resize = true }),
|
||||
require("mini.clue").gen_clues.z(),
|
||||
}
|
||||
'';
|
||||
triggers = [
|
||||
{
|
||||
mode = "n";
|
||||
keys = "<Leader>";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "<Leader>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "[";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "]";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "[";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "]";
|
||||
}
|
||||
{
|
||||
mode = "i";
|
||||
keys = "<C-x>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "g";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "g";
|
||||
}
|
||||
|
||||
{
|
||||
mode = "n";
|
||||
keys = "\"";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "\"";
|
||||
}
|
||||
{
|
||||
mode = "i";
|
||||
keys = "<C-r>";
|
||||
}
|
||||
{
|
||||
mode = "c";
|
||||
keys = "<C-r>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "<C-w>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "z";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "z";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "'";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "`";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "'";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "`";
|
||||
}
|
||||
];
|
||||
};
|
||||
cmdline = {};
|
||||
comment = {};
|
||||
diff = {};
|
||||
extra = {};
|
||||
git = {};
|
||||
hipatterns = {
|
||||
highlighters = {
|
||||
fixme.__raw = "{ pattern = '%f[%w]()FIXME()%f[%W]', group = 'MiniHipatternsFixme' }";
|
||||
hack.__raw = "{ pattern = '%f[%w]()HACK()%f[%W]', group = 'MiniHipatternsHack' }";
|
||||
todo.__raw = "{ pattern = '%f[%w]()TODO()%f[%W]', group = 'MiniHipatternsTodo' }";
|
||||
note.__raw = "{ pattern = '%f[%w]()NOTE()%f[%W]', group = 'MiniHipatternsNote' }";
|
||||
hex_color.__raw = "require('mini.hipatterns').gen_highlighter.hex_color()";
|
||||
};
|
||||
};
|
||||
icons = {};
|
||||
indentscope = {
|
||||
settings = {
|
||||
symbol = "|";
|
||||
};
|
||||
};
|
||||
jump = {};
|
||||
jump2d = {
|
||||
settings = {
|
||||
spotter.__raw = "require('mini.jump2d').gen_spotter.pattern('[^%s%p]+')";
|
||||
labels = "asdfghjkl";
|
||||
view = {
|
||||
dim = true;
|
||||
n_steps_ahead = 2;
|
||||
programs.nixvim = {
|
||||
plugins.mini = {
|
||||
enable = true;
|
||||
modules = {
|
||||
ai = {
|
||||
custom_textobjects = {
|
||||
B.__raw = "require('mini.extra').gen_ai_spec.buffer()";
|
||||
F.__raw = "require('mini.ai').gen_spec.treesitter({ a = '@function.outer', i = '@function.inner' })";
|
||||
};
|
||||
};
|
||||
align = {};
|
||||
basics = {
|
||||
options = {
|
||||
basic = true;
|
||||
extra_ui = true;
|
||||
};
|
||||
mappings = {
|
||||
basic = false;
|
||||
};
|
||||
autocommands = {
|
||||
basic = true;
|
||||
};
|
||||
};
|
||||
bracketed = {};
|
||||
clue = {
|
||||
clues.__raw = ''
|
||||
{
|
||||
{ mode = 'n', keys = '<Leader>e', desc = '+Explore/+Edit' },
|
||||
{ mode = 'n', keys = '<Leader>f', desc = '+Find' },
|
||||
{ mode = 'n', keys = '<Leader>v', desc = '+VCS' },
|
||||
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
||||
{ mode = 'x', keys = '<Leader>l', desc = '+LSP' },
|
||||
{ mode = 'n', keys = '<Leader>o', desc = '+OpenCode' },
|
||||
{ mode = 'x', keys = '<Leader>o', desc = '+OpenCode' },
|
||||
{ mode = 'n', keys = '<Leader>r', desc = '+Review' },
|
||||
{ mode = 'v', keys = '<Leader>r', desc = '+Review' },
|
||||
require("mini.clue").gen_clues.builtin_completion(),
|
||||
require("mini.clue").gen_clues.g(),
|
||||
require("mini.clue").gen_clues.marks(),
|
||||
require("mini.clue").gen_clues.registers(),
|
||||
require("mini.clue").gen_clues.windows({ submode_resize = true }),
|
||||
require("mini.clue").gen_clues.z(),
|
||||
}
|
||||
'';
|
||||
triggers = [
|
||||
{
|
||||
mode = "n";
|
||||
keys = "<Leader>";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "<Leader>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "[";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "]";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "[";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "]";
|
||||
}
|
||||
{
|
||||
mode = "i";
|
||||
keys = "<C-x>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "g";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "g";
|
||||
}
|
||||
|
||||
{
|
||||
mode = "n";
|
||||
keys = "\"";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "\"";
|
||||
}
|
||||
{
|
||||
mode = "i";
|
||||
keys = "<C-r>";
|
||||
}
|
||||
{
|
||||
mode = "c";
|
||||
keys = "<C-r>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "<C-w>";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "z";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "z";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "'";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
keys = "`";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "'";
|
||||
}
|
||||
{
|
||||
mode = "x";
|
||||
keys = "`";
|
||||
}
|
||||
];
|
||||
};
|
||||
cmdline = {};
|
||||
comment = {};
|
||||
diff = {};
|
||||
extra = {};
|
||||
git = {};
|
||||
hipatterns = {
|
||||
highlighters = {
|
||||
fixme.__raw = "{ pattern = '%f[%w]()FIXME()%f[%W]', group = 'MiniHipatternsFixme' }";
|
||||
hack.__raw = "{ pattern = '%f[%w]()HACK()%f[%W]', group = 'MiniHipatternsHack' }";
|
||||
todo.__raw = "{ pattern = '%f[%w]()TODO()%f[%W]', group = 'MiniHipatternsTodo' }";
|
||||
note.__raw = "{ pattern = '%f[%w]()NOTE()%f[%W]', group = 'MiniHipatternsNote' }";
|
||||
hex_color.__raw = "require('mini.hipatterns').gen_highlighter.hex_color()";
|
||||
};
|
||||
};
|
||||
icons = {};
|
||||
indentscope = {
|
||||
settings = {
|
||||
symbol = "|";
|
||||
};
|
||||
};
|
||||
jump = {};
|
||||
jump2d = {
|
||||
settings = {
|
||||
spotter.__raw = "require('mini.jump2d').gen_spotter.pattern('[^%s%p]+')";
|
||||
labels = "asdfghjkl";
|
||||
view = {
|
||||
dim = true;
|
||||
n_steps_ahead = 2;
|
||||
};
|
||||
};
|
||||
};
|
||||
move = {};
|
||||
notify = {
|
||||
content.format.__raw = ''
|
||||
function(notif)
|
||||
local formatted = MiniNotify.default_format(notif)
|
||||
return '\n ' .. formatted:gsub('\n', ' \n ') .. ' \n'
|
||||
end
|
||||
'';
|
||||
window.config = {
|
||||
border = "none";
|
||||
title = "";
|
||||
};
|
||||
};
|
||||
pairs = {};
|
||||
pick = {};
|
||||
splitjoin = {};
|
||||
starter = {};
|
||||
statusline = {
|
||||
content.active.__raw = ''
|
||||
function()
|
||||
local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
|
||||
local diff = MiniStatusline.section_diff({ trunc_width = 75 })
|
||||
local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 })
|
||||
local lsp = MiniStatusline.section_lsp({ trunc_width = 75 })
|
||||
local filename = MiniStatusline.section_filename({ trunc_width = 140 })
|
||||
local search = MiniStatusline.section_searchcount({ trunc_width = 75 })
|
||||
|
||||
return _G.CschStatusline.active({
|
||||
mode = mode,
|
||||
mode_hl = mode_hl,
|
||||
diff = diff,
|
||||
diagnostics = diagnostics,
|
||||
lsp = lsp,
|
||||
filename = filename,
|
||||
search = search,
|
||||
})
|
||||
end
|
||||
'';
|
||||
};
|
||||
surround = {};
|
||||
trailspace = {};
|
||||
visits = {};
|
||||
};
|
||||
move = {};
|
||||
notify = {};
|
||||
pairs = {};
|
||||
pick = {};
|
||||
splitjoin = {};
|
||||
starter = {};
|
||||
statusline = {};
|
||||
surround = {};
|
||||
trailspace = {};
|
||||
visits = {};
|
||||
mockDevIcons = true;
|
||||
};
|
||||
mockDevIcons = true;
|
||||
|
||||
extraConfigLua = ''
|
||||
local mini_notify_group = vim.api.nvim_create_augroup('MiniNotifyDesign', { clear = true })
|
||||
_G.CschStatusline = _G.CschStatusline or {}
|
||||
|
||||
local function to_hex(value)
|
||||
return value and string.format('#%06x', value) or nil
|
||||
end
|
||||
|
||||
local function get_hl(name)
|
||||
return vim.api.nvim_get_hl(0, { name = name, link = false })
|
||||
end
|
||||
|
||||
local function get_fg(name, fallback)
|
||||
return to_hex(get_hl(name).fg) or fallback
|
||||
end
|
||||
|
||||
local function get_bg(name, fallback)
|
||||
return to_hex(get_hl(name).bg) or fallback
|
||||
end
|
||||
|
||||
local function set_statusline_highlights()
|
||||
local block_bg = get_bg('CursorLine', get_bg('Visual', '#373b41'))
|
||||
local block_fg = get_fg('StatusLine', get_fg('Normal', '#c5c8c6'))
|
||||
|
||||
vim.api.nvim_set_hl(0, 'CschStatuslineBlock', { fg = block_fg, bg = block_bg })
|
||||
end
|
||||
|
||||
local function statusline_group(hl, strings)
|
||||
local parts = vim.tbl_filter(function(x)
|
||||
return type(x) == 'string' and x ~= ""
|
||||
end, strings or {})
|
||||
|
||||
if #parts == 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
return string.format('%%#%s# %s ', hl, table.concat(parts, ' '))
|
||||
end
|
||||
|
||||
local function statusline_block(text, hl)
|
||||
if text == nil or text == "" then
|
||||
return ""
|
||||
end
|
||||
|
||||
return string.format('%%#%s# %s ', hl, text)
|
||||
end
|
||||
|
||||
local function statusline_filesize()
|
||||
local size = math.max(vim.fn.line2byte(vim.fn.line('$') + 1) - 1, 0)
|
||||
|
||||
if size < 1024 then
|
||||
return string.format('%dB', size)
|
||||
elseif size < 1048576 then
|
||||
return string.format('%.2fKiB', size / 1024)
|
||||
end
|
||||
|
||||
return string.format('%.2fMiB', size / 1048576)
|
||||
end
|
||||
|
||||
local function statusline_filetype()
|
||||
local filetype = vim.bo.filetype
|
||||
|
||||
if filetype == "" then
|
||||
return vim.bo.buftype ~= "" and vim.bo.buftype or 'text'
|
||||
end
|
||||
|
||||
local icon = ""
|
||||
if _G.MiniIcons ~= nil then
|
||||
icon = _G.MiniIcons.get('filetype', filetype) or ""
|
||||
end
|
||||
|
||||
return (icon ~= "" and (icon .. ' ') or "") .. filetype
|
||||
end
|
||||
|
||||
local function statusline_fileinfo()
|
||||
local label = statusline_filetype()
|
||||
|
||||
if MiniStatusline.is_truncated(120) or vim.bo.buftype ~= "" then
|
||||
return label
|
||||
end
|
||||
|
||||
return string.format('%s · %s', label, statusline_filesize())
|
||||
end
|
||||
|
||||
local function statusline_location()
|
||||
local line = vim.fn.line('.')
|
||||
local total_lines = vim.fn.line('$')
|
||||
local column = vim.fn.virtcol('.')
|
||||
|
||||
if MiniStatusline.is_truncated(90) then
|
||||
return string.format('Ln %d Col %d', line, column)
|
||||
end
|
||||
|
||||
return string.format('Ln %d/%d · Col %d', line, total_lines, column)
|
||||
end
|
||||
|
||||
function _G.CschStatusline.active(parts)
|
||||
local left = vim.tbl_filter(function(x)
|
||||
return x ~= ""
|
||||
end, {
|
||||
statusline_block(parts.mode, parts.mode_hl),
|
||||
statusline_group('MiniStatuslineDevinfo', { parts.diff, parts.diagnostics, parts.lsp }),
|
||||
'%<',
|
||||
statusline_group('MiniStatuslineFilename', { parts.filename }),
|
||||
})
|
||||
local right = vim.tbl_filter(function(x)
|
||||
return x ~= ""
|
||||
end, {
|
||||
statusline_block(statusline_fileinfo(), 'CschStatuslineBlock'),
|
||||
statusline_block(parts.search, 'CschStatuslineBlock'),
|
||||
statusline_block(statusline_location(), parts.mode_hl),
|
||||
})
|
||||
|
||||
return table.concat(left, "") .. '%=%#StatusLine#' .. table.concat(right, "")
|
||||
end
|
||||
|
||||
local function set_mini_notify_highlights()
|
||||
local border = vim.api.nvim_get_hl(0, { name = 'FloatBorder' })
|
||||
local normal = vim.api.nvim_get_hl(0, { name = 'NormalFloat' })
|
||||
local popup_bg = get_bg('Pmenu', get_bg('CursorLine', get_bg('NormalFloat', '#303446')))
|
||||
local title = vim.api.nvim_get_hl(0, { name = 'FloatTitle' })
|
||||
|
||||
border.bg = 'NONE'
|
||||
normal.bg = popup_bg
|
||||
normal.bold = true
|
||||
title.bg = 'NONE'
|
||||
|
||||
vim.api.nvim_set_hl(0, 'MiniNotifyBorder', border)
|
||||
vim.api.nvim_set_hl(0, 'MiniNotifyNormal', normal)
|
||||
vim.api.nvim_set_hl(0, 'MiniNotifyTitle', title)
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd('ColorScheme', {
|
||||
group = mini_notify_group,
|
||||
callback = function()
|
||||
set_mini_notify_highlights()
|
||||
set_statusline_highlights()
|
||||
end,
|
||||
})
|
||||
|
||||
set_mini_notify_highlights()
|
||||
set_statusline_highlights()
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,36 @@ in {
|
||||
render-markdown-nvim
|
||||
];
|
||||
extraConfigLua = ''
|
||||
local api = vim.api
|
||||
local opencode_output_filetype = 'opencode_output'
|
||||
local opencode_window_filetypes = {
|
||||
opencode = true,
|
||||
opencode_output = true,
|
||||
}
|
||||
|
||||
local palette = {
|
||||
base = '#faf4ed',
|
||||
surface = '#fffaf3',
|
||||
overlay = '#f2e9e1',
|
||||
highlight_med = '#dfdad9',
|
||||
text = '#575279',
|
||||
subtle = '#797593',
|
||||
muted = '#9893a5',
|
||||
pine = '#286983',
|
||||
iris = '#907aa9',
|
||||
foam = '#56949f',
|
||||
leaf = '#6d8f89',
|
||||
gold = '#ea9d34',
|
||||
rose = '#d7827e',
|
||||
love = '#b4637a',
|
||||
}
|
||||
|
||||
local function set_highlights(highlights)
|
||||
for group, spec in pairs(highlights) do
|
||||
api.nvim_set_hl(0, group, spec)
|
||||
end
|
||||
end
|
||||
|
||||
local opencode_markdown_conceal_query = vim.treesitter.query.parse('markdown_inline', [[
|
||||
[
|
||||
(emphasis_delimiter)
|
||||
@@ -66,8 +96,28 @@ in {
|
||||
] @conceal)
|
||||
]])
|
||||
|
||||
local function collect_conceal_marks(ctx)
|
||||
local marks = {}
|
||||
|
||||
for _, node in opencode_markdown_conceal_query:iter_captures(ctx.root, ctx.buf) do
|
||||
local start_row, start_col, end_row, end_col = node:range()
|
||||
marks[#marks + 1] = {
|
||||
conceal = true,
|
||||
start_row = start_row,
|
||||
start_col = start_col,
|
||||
opts = {
|
||||
end_row = end_row,
|
||||
end_col = end_col,
|
||||
conceal = "",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return marks
|
||||
end
|
||||
|
||||
local function set_opencode_output_conceal()
|
||||
if vim.bo.filetype ~= 'opencode_output' then
|
||||
if vim.bo.filetype ~= opencode_output_filetype then
|
||||
return
|
||||
end
|
||||
|
||||
@@ -75,101 +125,172 @@ in {
|
||||
vim.wo.concealcursor = 'nvic'
|
||||
end
|
||||
|
||||
vim.treesitter.language.register('markdown', 'opencode_output')
|
||||
vim.treesitter.language.register('markdown_inline', 'opencode_output')
|
||||
local function hide_opencode_statusline()
|
||||
if not opencode_window_filetypes[vim.bo.filetype] then
|
||||
return
|
||||
end
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter' }, {
|
||||
vim.wo.statusline = ' '
|
||||
end
|
||||
|
||||
vim.treesitter.language.register('markdown', opencode_output_filetype)
|
||||
vim.treesitter.language.register('markdown_inline', opencode_output_filetype)
|
||||
|
||||
api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter' }, {
|
||||
callback = set_opencode_output_conceal,
|
||||
})
|
||||
api.nvim_create_autocmd({ 'FileType', 'BufWinEnter', 'WinEnter', 'BufEnter' }, {
|
||||
pattern = '*',
|
||||
callback = hide_opencode_statusline,
|
||||
})
|
||||
|
||||
require('render-markdown').setup({
|
||||
set_highlights({
|
||||
RenderMarkdownCode = { bg = palette.surface },
|
||||
RenderMarkdownCodeBorder = { fg = palette.highlight_med, bg = palette.surface },
|
||||
RenderMarkdownCodeInline = { bg = palette.surface },
|
||||
RenderMarkdownH1 = { fg = palette.pine, bold = true },
|
||||
RenderMarkdownH2 = { fg = palette.iris, bold = true },
|
||||
RenderMarkdownH3 = { fg = palette.foam, bold = true },
|
||||
RenderMarkdownH4 = { fg = palette.gold, bold = true },
|
||||
OpencodeInputLegend = { fg = palette.subtle, bold = true },
|
||||
OpencodeAgentBuild = { bg = palette.muted, fg = palette.base, bold = true },
|
||||
})
|
||||
|
||||
local render_markdown_config = {
|
||||
anti_conceal = { enabled = false },
|
||||
heading = {
|
||||
icons = { '◆ ', '◇ ', '○ ', '· ', '· ', '· ' },
|
||||
backgrounds = {},
|
||||
position = 'inline',
|
||||
width = 'block',
|
||||
left_pad = 0,
|
||||
right_pad = 2,
|
||||
border = false,
|
||||
sign = false,
|
||||
},
|
||||
code = {
|
||||
sign = false,
|
||||
width = 'full',
|
||||
left_pad = 2,
|
||||
right_pad = 0,
|
||||
border = 'thin',
|
||||
language_icon = false,
|
||||
language_name = true,
|
||||
},
|
||||
bullet = {
|
||||
icons = { '·', '–', '·', '–' },
|
||||
},
|
||||
custom_handlers = {
|
||||
markdown_inline = {
|
||||
extends = true,
|
||||
parse = function(ctx)
|
||||
local marks = {}
|
||||
|
||||
for _, node in opencode_markdown_conceal_query:iter_captures(ctx.root, ctx.buf) do
|
||||
local start_row, start_col, end_row, end_col = node:range()
|
||||
marks[#marks + 1] = {
|
||||
conceal = true,
|
||||
start_row = start_row,
|
||||
start_col = start_col,
|
||||
opts = {
|
||||
end_row = end_row,
|
||||
end_col = end_col,
|
||||
conceal = "",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return marks
|
||||
end,
|
||||
parse = collect_conceal_marks,
|
||||
},
|
||||
},
|
||||
file_types = { 'opencode_output' },
|
||||
file_types = { opencode_output_filetype },
|
||||
win_options = {
|
||||
conceallevel = { rendered = 3 },
|
||||
concealcursor = { rendered = "nvic" },
|
||||
concealcursor = { rendered = 'nvic' },
|
||||
},
|
||||
}
|
||||
|
||||
require('render-markdown').setup(render_markdown_config)
|
||||
|
||||
local opencode_icon_overrides = {
|
||||
header_user = '│',
|
||||
header_assistant = '│',
|
||||
run = '▸',
|
||||
task = '◦',
|
||||
read = '◦',
|
||||
edit = '◦',
|
||||
write = '◦',
|
||||
plan = '◦',
|
||||
search = '◦',
|
||||
web = '◦',
|
||||
list = '◦',
|
||||
tool = '◦',
|
||||
snapshot = '◦',
|
||||
restore_point = '◦',
|
||||
file = '·',
|
||||
folder = '·',
|
||||
attached_file = '·',
|
||||
agent = '·',
|
||||
reference = '·',
|
||||
reasoning = '◦',
|
||||
question = '?',
|
||||
border = '│',
|
||||
}
|
||||
|
||||
require('opencode').setup({
|
||||
server = {
|
||||
url = 'http://127.0.0.1',
|
||||
port = 18822,
|
||||
auto_kill = false,
|
||||
},
|
||||
input = {
|
||||
text = {
|
||||
wrap = true,
|
||||
},
|
||||
},
|
||||
debug = {
|
||||
show_ids = false,
|
||||
},
|
||||
ui = {
|
||||
icons = {
|
||||
preset = 'nerdfonts',
|
||||
overrides = opencode_icon_overrides,
|
||||
},
|
||||
},
|
||||
})
|
||||
require('opencode').setup({
|
||||
server = {
|
||||
url = 'http://127.0.0.1',
|
||||
port = 18822,
|
||||
auto_kill = false,
|
||||
},
|
||||
input = {
|
||||
text = {
|
||||
wrap = true,
|
||||
},
|
||||
},
|
||||
ui = {
|
||||
icons = {
|
||||
preset = 'nerdfonts',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
do
|
||||
local config = require('opencode.config')
|
||||
local formatter = require('opencode.ui.formatter')
|
||||
local format_utils = require('opencode.ui.formatter.utils')
|
||||
local icons = require('opencode.ui.icons')
|
||||
local util = require('opencode.util')
|
||||
do
|
||||
local config = require('opencode.config')
|
||||
local formatter = require('opencode.ui.formatter')
|
||||
local format_utils = require('opencode.ui.formatter.utils')
|
||||
local icons = require('opencode.ui.icons')
|
||||
local util = require('opencode.util')
|
||||
|
||||
formatter._format_reasoning = function(output, part)
|
||||
local text = vim.trim(part.text or "")
|
||||
local start_line = output:get_line_count() + 1
|
||||
local function format_reasoning_title(part)
|
||||
local title = 'Reasoning'
|
||||
local time = part.time
|
||||
|
||||
local title = 'Reasoning'
|
||||
local time = part.time
|
||||
if time and type(time) == 'table' and time.start then
|
||||
local duration_text = util.format_duration_seconds(time.start, time['end'])
|
||||
if duration_text then
|
||||
title = string.format('%s %s', title, duration_text)
|
||||
end
|
||||
end
|
||||
|
||||
format_utils.format_action(output, icons.get('reasoning'), title, "")
|
||||
|
||||
if config.ui.output.tools.show_reasoning_output and text ~= "" then
|
||||
output:add_empty_line()
|
||||
output:add_lines(vim.split(text, '\n'), ' ')
|
||||
output:add_empty_line()
|
||||
end
|
||||
|
||||
local end_line = output:get_line_count()
|
||||
if end_line - start_line > 1 then
|
||||
formatter.add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')
|
||||
else
|
||||
output:add_extmark(start_line - 1, {
|
||||
line_hl_group = 'OpencodeReasoningText',
|
||||
})
|
||||
if time and type(time) == 'table' and time.start then
|
||||
local duration_text = util.format_duration_seconds(time.start, time['end'])
|
||||
if duration_text then
|
||||
title = string.format('%s %s', title, duration_text)
|
||||
end
|
||||
end
|
||||
|
||||
return title
|
||||
end
|
||||
|
||||
local function highlight_reasoning_block(output, start_line)
|
||||
local end_line = output:get_line_count()
|
||||
|
||||
if end_line - start_line > 1 then
|
||||
formatter.add_vertical_border(output, start_line, end_line, 'OpencodeToolBorder', -1, 'OpencodeReasoningText')
|
||||
return
|
||||
end
|
||||
|
||||
output:add_extmark(start_line - 1, {
|
||||
line_hl_group = 'OpencodeReasoningText',
|
||||
})
|
||||
end
|
||||
|
||||
formatter._format_reasoning = function(output, part)
|
||||
local text = vim.trim(part.text or "")
|
||||
local start_line = output:get_line_count() + 1
|
||||
|
||||
format_utils.format_action(output, icons.get('reasoning'), format_reasoning_title(part), "")
|
||||
|
||||
if config.ui.output.tools.show_reasoning_output and text ~= "" then
|
||||
output:add_empty_line()
|
||||
output:add_lines(vim.split(text, '\n'), ' ')
|
||||
output:add_empty_line()
|
||||
end
|
||||
|
||||
highlight_reasoning_block(output, start_line)
|
||||
end
|
||||
end
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,6 +35,26 @@
|
||||
end, { force = true, all = true })
|
||||
end
|
||||
end
|
||||
|
||||
-- Fix grammar-bundled treesitter queries that use #match? with Lua pattern
|
||||
-- syntax (e.g. %d) instead of Vim regex. Neovim 0.11 picks the first
|
||||
-- non-extending query file in the rtp as the base, so the grammar-bundled
|
||||
-- (buggy) queries take precedence over the corrected site-level queries.
|
||||
-- Override affected languages with the site-level version.
|
||||
do
|
||||
local langs = { "sql" }
|
||||
for _, lang in ipairs(langs) do
|
||||
local files = vim.api.nvim_get_runtime_file(
|
||||
"queries/" .. lang .. "/highlights.scm", true)
|
||||
if #files > 1 then
|
||||
local f = io.open(files[#files])
|
||||
if f then
|
||||
vim.treesitter.query.set(lang, "highlights", f:read("*all"))
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
settings = {
|
||||
model = "anthropic/claude-opus-4-6";
|
||||
small_model = "anthropic/claude-haiku-4-5";
|
||||
theme = "catppuccin";
|
||||
theme = "rosepine";
|
||||
plugin = ["opencode-anthropic-auth@latest"];
|
||||
permission = {
|
||||
read = {
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
};
|
||||
|
||||
nix = {
|
||||
settings.trusted-users = ["@admin" "cschmatzler"];
|
||||
settings.trusted-users = ["cschmatzler"];
|
||||
gc.interval = {
|
||||
Weekday = 0;
|
||||
Hour = 2;
|
||||
|
||||
@@ -23,16 +23,19 @@
|
||||
|
||||
config = {
|
||||
flake.flakeModules = {
|
||||
# Shared system foundations
|
||||
core = ./core.nix;
|
||||
network = ./network.nix;
|
||||
nixos-system = ./nixos-system.nix;
|
||||
|
||||
# User environment
|
||||
ai-tools = ./ai-tools.nix;
|
||||
atuin = ./atuin.nix;
|
||||
core = ./core.nix;
|
||||
desktop = ./desktop.nix;
|
||||
dev-tools = ./dev-tools.nix;
|
||||
email = ./email.nix;
|
||||
finance = ./finance.nix;
|
||||
neovim = ./neovim.nix;
|
||||
network = ./network.nix;
|
||||
nixos-system = ./nixos-system.nix;
|
||||
shell = ./shell.nix;
|
||||
ssh-client = ./ssh-client.nix;
|
||||
terminal = ./terminal.nix;
|
||||
|
||||
@@ -316,36 +316,36 @@
|
||||
|
||||
gui = {
|
||||
authorColors = {
|
||||
"*" = "#7287fd";
|
||||
"*" = "#907aa9";
|
||||
};
|
||||
theme = {
|
||||
activeBorderColor = [
|
||||
"#8839ef"
|
||||
"#907aa9"
|
||||
"bold"
|
||||
];
|
||||
inactiveBorderColor = [
|
||||
"#6c6f85"
|
||||
"#9893a5"
|
||||
];
|
||||
optionsTextColor = [
|
||||
"#1e66f5"
|
||||
"#286983"
|
||||
];
|
||||
selectedLineBgColor = [
|
||||
"#ccd0da"
|
||||
"#f2e9e1"
|
||||
];
|
||||
cherryPickedCommitBgColor = [
|
||||
"#bcc0cc"
|
||||
"#dfdad9"
|
||||
];
|
||||
cherryPickedCommitFgColor = [
|
||||
"#8839ef"
|
||||
"#907aa9"
|
||||
];
|
||||
defaultFgColor = [
|
||||
"#4c4f69"
|
||||
"#575279"
|
||||
];
|
||||
searchingActiveBorderColor = [
|
||||
"#df8e1d"
|
||||
"#ea9d34"
|
||||
];
|
||||
unstagedChangesColor = [
|
||||
"#d20f39"
|
||||
"#b4637a"
|
||||
];
|
||||
};
|
||||
};
|
||||
@@ -378,21 +378,23 @@
|
||||
ast-grep
|
||||
bun
|
||||
delta
|
||||
deadnix
|
||||
devenv
|
||||
docker
|
||||
docker-compose
|
||||
gh
|
||||
git
|
||||
gnumake
|
||||
hyperfine
|
||||
jj-ryu
|
||||
jj-starship
|
||||
nil
|
||||
nodejs_24
|
||||
nurl
|
||||
pnpm
|
||||
postgresql_17
|
||||
serie
|
||||
sqlite
|
||||
statix
|
||||
tea
|
||||
tokei
|
||||
tree-sitter
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
address = "christoph@schmatzler.com";
|
||||
userName = "christoph.schmatzler@icloud.com";
|
||||
realName = "Christoph Schmatzler";
|
||||
passwordCommand = ["cat" "/run/secrets/tahani-email-password"];
|
||||
passwordCommand = ["${pkgs.coreutils}/bin/cat" "/run/secrets/tahani-email-password"];
|
||||
folders = {
|
||||
inbox = "INBOX";
|
||||
drafts = "Drafts";
|
||||
|
||||
@@ -11,180 +11,16 @@
|
||||
den.aspects.tailscale
|
||||
];
|
||||
|
||||
den.aspects.michael.nixos = {
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
modulesPath,
|
||||
...
|
||||
}: {
|
||||
den.aspects.michael.nixos = {modulesPath, ...}: {
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
./_hosts/michael/backups.nix
|
||||
./_hosts/michael/disk-config.nix
|
||||
./_hosts/michael/gitea.nix
|
||||
./_hosts/michael/hardware-configuration.nix
|
||||
inputs.disko.nixosModules.default
|
||||
];
|
||||
|
||||
networking.hostName = "michael";
|
||||
|
||||
sops.secrets = {
|
||||
michael-gitea-litestream = {
|
||||
sopsFile = ../secrets/michael-gitea-litestream;
|
||||
format = "binary";
|
||||
owner = "gitea";
|
||||
group = "gitea";
|
||||
};
|
||||
michael-gitea-restic-password = {
|
||||
sopsFile = ../secrets/michael-gitea-restic-password;
|
||||
format = "binary";
|
||||
owner = "gitea";
|
||||
group = "gitea";
|
||||
};
|
||||
michael-gitea-restic-env = {
|
||||
sopsFile = ../secrets/michael-gitea-restic-env;
|
||||
format = "binary";
|
||||
owner = "gitea";
|
||||
group = "gitea";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [80 443];
|
||||
|
||||
services.redis.servers.gitea = {
|
||||
enable = true;
|
||||
port = 6380;
|
||||
bind = "127.0.0.1";
|
||||
settings = {
|
||||
maxmemory = "64mb";
|
||||
maxmemory-policy = "allkeys-lru";
|
||||
};
|
||||
};
|
||||
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
database = {
|
||||
type = "sqlite3";
|
||||
path = "/var/lib/gitea/data/gitea.db";
|
||||
};
|
||||
settings = {
|
||||
server = {
|
||||
ROOT_URL = "https://git.schmatzler.com/";
|
||||
DOMAIN = "git.schmatzler.com";
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
HTTP_PORT = 3000;
|
||||
LANDING_PAGE = "explore";
|
||||
};
|
||||
service.DISABLE_REGISTRATION = true;
|
||||
security.INSTALL_LOCK = true;
|
||||
cache = {
|
||||
ADAPTER = "redis";
|
||||
HOST = "redis://127.0.0.1:6380/0?pool_size=100&idle_timeout=180s";
|
||||
ITEM_TTL = "16h";
|
||||
};
|
||||
"cache.last_commit" = {
|
||||
ITEM_TTL = "8760h";
|
||||
COMMITS_COUNT = 100;
|
||||
};
|
||||
session = {
|
||||
PROVIDER = "redis";
|
||||
PROVIDER_CONFIG = "redis://127.0.0.1:6380/1?pool_size=100&idle_timeout=180s";
|
||||
COOKIE_SECURE = true;
|
||||
SAME_SITE = "strict";
|
||||
};
|
||||
api.ENABLE_SWAGGER = false;
|
||||
};
|
||||
};
|
||||
|
||||
services.litestream = {
|
||||
enable = true;
|
||||
environmentFile = config.sops.secrets.michael-gitea-litestream.path;
|
||||
settings = {
|
||||
dbs = [
|
||||
{
|
||||
path = "/var/lib/gitea/data/gitea.db";
|
||||
replicas = [
|
||||
{
|
||||
type = "s3";
|
||||
bucket = "michael-gitea-litestream";
|
||||
path = "gitea";
|
||||
endpoint = "s3.eu-central-003.backblazeb2.com";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.litestream = {
|
||||
serviceConfig = {
|
||||
User = lib.mkForce "gitea";
|
||||
Group = lib.mkForce "gitea";
|
||||
};
|
||||
};
|
||||
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
virtualHosts."git.schmatzler.com".extraConfig = ''
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
}
|
||||
reverse_proxy localhost:3000
|
||||
'';
|
||||
};
|
||||
|
||||
services.restic.backups.gitea = {
|
||||
repository = "s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories";
|
||||
paths = ["/var/lib/gitea"];
|
||||
exclude = [
|
||||
"/var/lib/gitea/log"
|
||||
"/var/lib/gitea/data/gitea.db"
|
||||
"/var/lib/gitea/data/gitea.db-shm"
|
||||
"/var/lib/gitea/data/gitea.db-wal"
|
||||
];
|
||||
passwordFile = config.sops.secrets.michael-gitea-restic-password.path;
|
||||
environmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
||||
pruneOpts = [
|
||||
"--keep-daily 7"
|
||||
"--keep-weekly 4"
|
||||
"--keep-monthly 6"
|
||||
];
|
||||
timerConfig = {
|
||||
OnCalendar = "daily";
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = "1h";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.restic-backups-gitea = {
|
||||
wants = ["restic-init-gitea.service"];
|
||||
after = ["restic-init-gitea.service"];
|
||||
serviceConfig = {
|
||||
User = lib.mkForce "gitea";
|
||||
Group = lib.mkForce "gitea";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.restic-init-gitea = {
|
||||
description = "Initialize Restic repository for Gitea backups";
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network-online.target"];
|
||||
wants = ["network-online.target"];
|
||||
path = [pkgs.restic];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "gitea";
|
||||
Group = "gitea";
|
||||
RemainAfterExit = true;
|
||||
EnvironmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
||||
};
|
||||
script = ''
|
||||
export RESTIC_PASSWORD=$(cat ${config.sops.secrets.michael-gitea-restic-password.path})
|
||||
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories snapshots &>/dev/null || \
|
||||
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories init
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
extraEnv =
|
||||
''
|
||||
$env.LS_COLORS = (${pkgs.vivid}/bin/vivid generate catppuccin-latte)
|
||||
$env.LS_COLORS = (${pkgs.vivid}/bin/vivid generate rose-pine-dawn)
|
||||
''
|
||||
+ lib.optionalString pkgs.stdenv.isDarwin ''
|
||||
# Nushell on Darwin doesn't source /etc/zprofile or path_helper,
|
||||
@@ -36,65 +36,55 @@
|
||||
'';
|
||||
|
||||
extraConfig = ''
|
||||
# --- Catppuccin Latte Theme ---
|
||||
# --- Rosé Pine Dawn Theme ---
|
||||
let theme = {
|
||||
rosewater: "#dc8a78"
|
||||
flamingo: "#dd7878"
|
||||
pink: "#ea76cb"
|
||||
mauve: "#8839ef"
|
||||
red: "#d20f39"
|
||||
maroon: "#e64553"
|
||||
peach: "#fe640b"
|
||||
yellow: "#df8e1d"
|
||||
green: "#40a02b"
|
||||
teal: "#179299"
|
||||
sky: "#04a5e5"
|
||||
sapphire: "#209fb5"
|
||||
blue: "#1e66f5"
|
||||
lavender: "#7287fd"
|
||||
text: "#4c4f69"
|
||||
subtext1: "#5c5f77"
|
||||
subtext0: "#6c6f85"
|
||||
overlay2: "#7c7f93"
|
||||
overlay1: "#8c8fa1"
|
||||
overlay0: "#9ca0b0"
|
||||
surface2: "#acb0be"
|
||||
surface1: "#bcc0cc"
|
||||
surface0: "#ccd0da"
|
||||
base: "#eff1f5"
|
||||
mantle: "#e6e9ef"
|
||||
crust: "#dce0e8"
|
||||
love: "#b4637a"
|
||||
gold: "#ea9d34"
|
||||
rose: "#d7827e"
|
||||
pine: "#286983"
|
||||
foam: "#56949f"
|
||||
iris: "#907aa9"
|
||||
leaf: "#6d8f89"
|
||||
text: "#575279"
|
||||
subtle: "#797593"
|
||||
muted: "#9893a5"
|
||||
highlight_high: "#cecacd"
|
||||
highlight_med: "#dfdad9"
|
||||
highlight_low: "#f4ede8"
|
||||
overlay: "#f2e9e1"
|
||||
surface: "#fffaf3"
|
||||
base: "#faf4ed"
|
||||
}
|
||||
|
||||
let scheme = {
|
||||
recognized_command: $theme.blue
|
||||
recognized_command: $theme.pine
|
||||
unrecognized_command: $theme.text
|
||||
constant: $theme.peach
|
||||
punctuation: $theme.overlay2
|
||||
operator: $theme.sky
|
||||
string: $theme.green
|
||||
virtual_text: $theme.surface2
|
||||
variable: { fg: $theme.flamingo attr: i }
|
||||
filepath: $theme.yellow
|
||||
constant: $theme.gold
|
||||
punctuation: $theme.muted
|
||||
operator: $theme.subtle
|
||||
string: $theme.gold
|
||||
virtual_text: $theme.highlight_high
|
||||
variable: { fg: $theme.rose attr: i }
|
||||
filepath: $theme.iris
|
||||
}
|
||||
|
||||
$env.config.color_config = {
|
||||
separator: { fg: $theme.surface2 attr: b }
|
||||
leading_trailing_space_bg: { fg: $theme.lavender attr: u }
|
||||
separator: { fg: $theme.highlight_high attr: b }
|
||||
leading_trailing_space_bg: { fg: $theme.iris attr: u }
|
||||
header: { fg: $theme.text attr: b }
|
||||
row_index: $scheme.virtual_text
|
||||
record: $theme.text
|
||||
list: $theme.text
|
||||
hints: $scheme.virtual_text
|
||||
search_result: { fg: $theme.base bg: $theme.yellow }
|
||||
shape_closure: $theme.teal
|
||||
closure: $theme.teal
|
||||
shape_flag: { fg: $theme.maroon attr: i }
|
||||
search_result: { fg: $theme.base bg: $theme.gold }
|
||||
shape_closure: $theme.foam
|
||||
closure: $theme.foam
|
||||
shape_flag: { fg: $theme.love attr: i }
|
||||
shape_matching_brackets: { attr: u }
|
||||
shape_garbage: $theme.red
|
||||
shape_keyword: $theme.mauve
|
||||
shape_match_pattern: $theme.green
|
||||
shape_signature: $theme.teal
|
||||
shape_garbage: $theme.love
|
||||
shape_keyword: $theme.iris
|
||||
shape_match_pattern: $theme.leaf
|
||||
shape_signature: $theme.foam
|
||||
shape_table: $scheme.punctuation
|
||||
cell-path: $scheme.punctuation
|
||||
shape_list: $scheme.punctuation
|
||||
@@ -104,53 +94,53 @@
|
||||
empty: { attr: n }
|
||||
filesize: {||
|
||||
if $in < 1kb {
|
||||
$theme.teal
|
||||
$theme.foam
|
||||
} else if $in < 10kb {
|
||||
$theme.green
|
||||
$theme.leaf
|
||||
} else if $in < 100kb {
|
||||
$theme.yellow
|
||||
$theme.gold
|
||||
} else if $in < 10mb {
|
||||
$theme.peach
|
||||
$theme.rose
|
||||
} else if $in < 100mb {
|
||||
$theme.maroon
|
||||
$theme.love
|
||||
} else if $in < 1gb {
|
||||
$theme.red
|
||||
$theme.love
|
||||
} else {
|
||||
$theme.mauve
|
||||
$theme.iris
|
||||
}
|
||||
}
|
||||
duration: {||
|
||||
if $in < 1day {
|
||||
$theme.teal
|
||||
$theme.foam
|
||||
} else if $in < 1wk {
|
||||
$theme.green
|
||||
$theme.leaf
|
||||
} else if $in < 4wk {
|
||||
$theme.yellow
|
||||
$theme.gold
|
||||
} else if $in < 12wk {
|
||||
$theme.peach
|
||||
$theme.rose
|
||||
} else if $in < 24wk {
|
||||
$theme.maroon
|
||||
$theme.love
|
||||
} else if $in < 52wk {
|
||||
$theme.red
|
||||
$theme.love
|
||||
} else {
|
||||
$theme.mauve
|
||||
$theme.iris
|
||||
}
|
||||
}
|
||||
datetime: {|| (date now) - $in |
|
||||
if $in < 1day {
|
||||
$theme.teal
|
||||
$theme.foam
|
||||
} else if $in < 1wk {
|
||||
$theme.green
|
||||
$theme.leaf
|
||||
} else if $in < 4wk {
|
||||
$theme.yellow
|
||||
$theme.gold
|
||||
} else if $in < 12wk {
|
||||
$theme.peach
|
||||
$theme.rose
|
||||
} else if $in < 24wk {
|
||||
$theme.maroon
|
||||
$theme.love
|
||||
} else if $in < 52wk {
|
||||
$theme.red
|
||||
$theme.love
|
||||
} else {
|
||||
$theme.mauve
|
||||
$theme.iris
|
||||
}
|
||||
}
|
||||
shape_external: $scheme.unrecognized_command
|
||||
@@ -158,11 +148,11 @@
|
||||
shape_external_resolved: $scheme.recognized_command
|
||||
shape_block: $scheme.recognized_command
|
||||
block: $scheme.recognized_command
|
||||
shape_custom: $theme.pink
|
||||
custom: $theme.pink
|
||||
shape_custom: $theme.rose
|
||||
custom: $theme.rose
|
||||
background: $theme.base
|
||||
foreground: $theme.text
|
||||
cursor: { bg: $theme.rosewater fg: $theme.base }
|
||||
cursor: { bg: $theme.text fg: $theme.base }
|
||||
shape_range: $scheme.operator
|
||||
range: $scheme.operator
|
||||
shape_pipe: $scheme.operator
|
||||
@@ -187,51 +177,51 @@
|
||||
shape_literal: $scheme.constant
|
||||
string: $scheme.string
|
||||
shape_string: $scheme.string
|
||||
shape_string_interpolation: $theme.flamingo
|
||||
shape_string_interpolation: $theme.rose
|
||||
shape_raw_string: $scheme.string
|
||||
shape_externalarg: $scheme.string
|
||||
}
|
||||
$env.config.highlight_resolved_externals = true
|
||||
$env.config.explore = {
|
||||
status_bar_background: { fg: $theme.text, bg: $theme.mantle },
|
||||
status_bar_background: { fg: $theme.text, bg: $theme.surface },
|
||||
command_bar_text: { fg: $theme.text },
|
||||
highlight: { fg: $theme.base, bg: $theme.yellow },
|
||||
highlight: { fg: $theme.base, bg: $theme.gold },
|
||||
status: {
|
||||
error: $theme.red,
|
||||
warn: $theme.yellow,
|
||||
info: $theme.blue,
|
||||
error: $theme.love,
|
||||
warn: $theme.gold,
|
||||
info: $theme.pine,
|
||||
},
|
||||
selected_cell: { bg: $theme.blue fg: $theme.base },
|
||||
selected_cell: { bg: $theme.pine fg: $theme.base },
|
||||
}
|
||||
|
||||
# --- Custom Commands ---
|
||||
def --env open_project [] {
|
||||
let base = ($env.HOME | path join "Projects")
|
||||
let choice = (
|
||||
${pkgs.fd}/bin/fd -t d -d 1 -a . ($base | path join "Personal") ($base | path join "Work")
|
||||
| lines
|
||||
| each {|p| $p | str replace $"($base)/" "" }
|
||||
| str join "\n"
|
||||
| ${pkgs.fzf}/bin/fzf --prompt "project > "
|
||||
)
|
||||
if ($choice | str trim | is-not-empty) {
|
||||
cd ($base | path join ($choice | str trim))
|
||||
}
|
||||
}
|
||||
|
||||
# --- Keybinding: Ctrl+O for open_project ---
|
||||
$env.config.keybindings = ($env.config.keybindings | append [
|
||||
{
|
||||
name: open_project
|
||||
modifier: control
|
||||
keycode: char_o
|
||||
mode: [emacs vi_insert vi_normal]
|
||||
event: {
|
||||
send: executehostcommand
|
||||
cmd: "open_project"
|
||||
# --- Custom Commands ---
|
||||
def --env open_project [] {
|
||||
let base = ($env.HOME | path join "Projects")
|
||||
let choice = (
|
||||
${pkgs.fd}/bin/fd -t d -d 1 -a . ($base | path join "Personal") ($base | path join "Work")
|
||||
| lines
|
||||
| each {|p| $p | str replace $"($base)/" "" }
|
||||
| str join "\n"
|
||||
| ${pkgs.fzf}/bin/fzf --prompt "project > "
|
||||
)
|
||||
if ($choice | str trim | is-not-empty) {
|
||||
cd ($base | path join ($choice | str trim))
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
# --- Keybinding: Ctrl+O for open_project ---
|
||||
$env.config.keybindings = ($env.config.keybindings | append [
|
||||
{
|
||||
name: open_project
|
||||
modifier: control
|
||||
keycode: char_o
|
||||
mode: [emacs vi_insert vi_normal]
|
||||
event: {
|
||||
send: executehostcommand
|
||||
cmd: "open_project"
|
||||
}
|
||||
}
|
||||
])
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -284,7 +274,6 @@
|
||||
|
||||
home.packages = with pkgs; [
|
||||
vivid
|
||||
(callPackage ./_lib/open-project.nix {})
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
{...}: {
|
||||
den.aspects.ssh-client.homeManager = {
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
}: let
|
||||
homeDir = "${
|
||||
if pkgs.stdenv.hostPlatform.isDarwin
|
||||
then "/Users"
|
||||
else "/home"
|
||||
}/${config.home.username}";
|
||||
in {
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
enableDefaultConfig = false;
|
||||
includes = [
|
||||
(lib.mkIf pkgs.stdenv.hostPlatform.isLinux "/home/${config.home.username}/.ssh/config_external")
|
||||
(lib.mkIf pkgs.stdenv.hostPlatform.isDarwin "/Users/${config.home.username}/.ssh/config_external")
|
||||
"${homeDir}/.ssh/config_external"
|
||||
];
|
||||
matchBlocks = {
|
||||
"*" = {};
|
||||
"github.com" = {
|
||||
identitiesOnly = true;
|
||||
identityFile = [
|
||||
(lib.mkIf pkgs.stdenv.hostPlatform.isLinux "/home/${config.home.username}/.ssh/id_ed25519")
|
||||
(lib.mkIf pkgs.stdenv.hostPlatform.isDarwin "/Users/${config.home.username}/.ssh/id_ed25519")
|
||||
"${homeDir}/.ssh/id_ed25519"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home.packages = [pkgs.openssh];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@
|
||||
tahani-paperless-password = {
|
||||
sopsFile = ../secrets/tahani-paperless-password;
|
||||
format = "binary";
|
||||
path = "/run/secrets/tahani-paperless-password";
|
||||
};
|
||||
tahani-email-password = {
|
||||
sopsFile = ../secrets/tahani-email-password;
|
||||
format = "binary";
|
||||
owner = "cschmatzler";
|
||||
path = "/run/secrets/tahani-email-password";
|
||||
};
|
||||
};
|
||||
virtualisation.docker.enable = true;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}: {
|
||||
xdg.configFile."ghostty/config".text = ''
|
||||
command = ${pkgs.nushell}/bin/nu
|
||||
theme = Catppuccin Latte
|
||||
theme = Rose Pine Dawn
|
||||
window-padding-x = 12
|
||||
window-padding-y = 3
|
||||
window-padding-balance = true
|
||||
@@ -24,19 +24,19 @@
|
||||
programs.bat = {
|
||||
enable = true;
|
||||
config = {
|
||||
theme = "Catppuccin Latte";
|
||||
theme = "Rosé Pine Dawn";
|
||||
pager = "ov";
|
||||
};
|
||||
themes = {
|
||||
"Catppuccin Latte" = {
|
||||
"Rosé Pine Dawn" = {
|
||||
src =
|
||||
pkgs.fetchFromGitHub {
|
||||
owner = "catppuccin";
|
||||
repo = "bat";
|
||||
rev = "6810349b28055dce54076712fc05fc68da4b8ec0";
|
||||
sha256 = "lJapSgRVENTrbmpVyn+UQabC9fpV1G1e+CdlJ090uvg=";
|
||||
owner = "rose-pine";
|
||||
repo = "tm-theme";
|
||||
rev = "23bb25b9c421cdc9ea89ff3ad3825840cd19d65d";
|
||||
hash = "sha256-GUFdv5V5OZ2PG+gfsbiohMT23LWsrZda34ReHBr2Xy0=";
|
||||
};
|
||||
file = "themes/Catppuccin Latte.tmTheme";
|
||||
file = "dist/rose-pine-dawn.tmTheme";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -55,11 +55,11 @@
|
||||
--preview-window='border-rounded' --prompt=' ' --marker=' ' --pointer=' '
|
||||
--separator='─' --scrollbar='┃' --layout='reverse'
|
||||
|
||||
--color=bg+:#CCD0DA,bg:#EFF1F5,spinner:#DC8A78,hl:#D20F39
|
||||
--color=fg:#4C4F69,header:#D20F39,info:#8839EF,pointer:#DC8A78
|
||||
--color=marker:#7287FD,fg+:#4C4F69,prompt:#8839EF,hl+:#D20F39
|
||||
--color=selected-bg:#BCC0CC
|
||||
--color=border:#9CA0B0,label:#4C4F69
|
||||
--color=bg+:#f2e9e1,bg:#faf4ed,spinner:#ea9d34,hl:#d7827e
|
||||
--color=fg:#797593,header:#286983,info:#56949f,pointer:#907aa9
|
||||
--color=marker:#b4637a,fg+:#575279,prompt:#797593,hl+:#d7827e
|
||||
--color=selected-bg:#f2e9e1
|
||||
--color=border:#dfdad9,label:#575279
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
programs.zellij = {
|
||||
enable = true;
|
||||
settings = {
|
||||
theme = "catppuccin-latte";
|
||||
theme = "rose-pine-dawn";
|
||||
default_layout = "default";
|
||||
default_shell = "${pkgs.nushell}/bin/nu";
|
||||
pane_frames = false;
|
||||
@@ -12,6 +12,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile."zellij/themes/rose-pine-dawn.kdl".text = ''
|
||||
themes {
|
||||
rose-pine-dawn {
|
||||
fg "#575279"
|
||||
bg "#f2e9e1"
|
||||
black "#faf4ed"
|
||||
red "#b4637a"
|
||||
green "#6d8f89"
|
||||
yellow "#ea9d34"
|
||||
blue "#286983"
|
||||
magenta "#907aa9"
|
||||
cyan "#56949f"
|
||||
white "#575279"
|
||||
orange "#d7827e"
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
xdg.configFile."zellij/layouts/default.kdl".text = ''
|
||||
layout {
|
||||
default_tab_template {
|
||||
@@ -23,26 +41,26 @@
|
||||
plugin location="file:${pkgs.zjstatus}/bin/zjstatus.wasm" {
|
||||
hide_frame_for_single_pane "true"
|
||||
|
||||
format_left "{mode}#[fg=#1e66f5,bg=#eff1f5,bold] {session}#[bg=#eff1f5] {tabs}"
|
||||
format_left "{mode}#[fg=#286983,bg=#faf4ed,bold] {session}#[bg=#faf4ed] {tabs}"
|
||||
format_right "{datetime}"
|
||||
format_space "#[bg=#eff1f5]"
|
||||
format_space "#[bg=#faf4ed]"
|
||||
|
||||
mode_normal "#[fg=#eff1f5,bg=#1e66f5] "
|
||||
mode_locked "#[fg=#eff1f5,bg=#fe640b] L "
|
||||
mode_tab "#[fg=#eff1f5,bg=#40a02b] T "
|
||||
mode_pane "#[fg=#eff1f5,bg=#8839ef] P "
|
||||
mode_session "#[fg=#eff1f5,bg=#04a5e5] S "
|
||||
mode_resize "#[fg=#eff1f5,bg=#df8e1d] R "
|
||||
mode_move "#[fg=#eff1f5,bg=#ea76cb] M "
|
||||
mode_search "#[fg=#eff1f5,bg=#d20f39] S "
|
||||
mode_normal "#[fg=#faf4ed,bg=#286983] "
|
||||
mode_locked "#[fg=#faf4ed,bg=#ea9d34] L "
|
||||
mode_tab "#[fg=#faf4ed,bg=#6d8f89] T "
|
||||
mode_pane "#[fg=#faf4ed,bg=#907aa9] P "
|
||||
mode_session "#[fg=#faf4ed,bg=#56949f] S "
|
||||
mode_resize "#[fg=#faf4ed,bg=#ea9d34] R "
|
||||
mode_move "#[fg=#faf4ed,bg=#d7827e] M "
|
||||
mode_search "#[fg=#faf4ed,bg=#b4637a] S "
|
||||
|
||||
tab_normal "#[fg=#acb0be,bg=#eff1f5] {index} {name} {fullscreen_indicator}{sync_indicator}{floating_indicator}"
|
||||
tab_active "#[fg=#eff1f5,bg=#1e66f5,bold,underline] {index} {name} {fullscreen_indicator}{sync_indicator}{floating_indicator}"
|
||||
tab_normal "#[fg=#9893a5,bg=#faf4ed] {index} {name} {fullscreen_indicator}{sync_indicator}{floating_indicator}"
|
||||
tab_active "#[fg=#faf4ed,bg=#286983,bold,underline] {index} {name} {fullscreen_indicator}{sync_indicator}{floating_indicator}"
|
||||
tab_fullscreen_indicator " "
|
||||
tab_sync_indicator " "
|
||||
tab_floating_indicator " "
|
||||
|
||||
datetime "#[fg=#4c4f69,bg=#eff1f5] {format} "
|
||||
datetime "#[fg=#575279,bg=#faf4ed] {format} "
|
||||
datetime_format "%A, %d %b %Y %H:%M"
|
||||
datetime_timezone "Europe/Berlin"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user