Totally new ai.nix, integrating MIstral with ZED

This commit is contained in:
2026-02-28 10:29:56 +01:00
parent 33618c1b07
commit 6dbfc8be6c
44 changed files with 2855 additions and 444 deletions
+447 -409
View File
File diff suppressed because it is too large Load Diff
+83 -26
View File
@@ -467,6 +467,7 @@ The tree below shows the full repository layout, with the standardized internal
├── assets ├── assets
│   ├── conf │   ├── conf
│   │   ├── apps │   │   ├── apps
│   │   ├── ai.nix
│   │   │   ├── flatpaks.conf │   │   │   ├── flatpaks.conf
│   │   │   └── packages.conf │   │   │   └── packages.conf
│   │   ├── core │   │   ├── core
@@ -524,7 +525,6 @@ The tree below shows the full repository layout, with the standardized internal
│   └── scripts │   └── scripts
├── configuration ├── configuration
│   ├── apps │   ├── apps
│   │   ├── ai.nix
│   │   ├── install_flatpaks.nix │   │   ├── install_flatpaks.nix
│   │   └── install_packages.nix │   │   └── install_packages.nix
│   ├── core │   ├── core
@@ -858,7 +858,6 @@ This section describes the main system configuration for the computers that I ha
{ pkgs, user, ... } : { pkgs, user, ... } :
{ {
imports = [ imports = [
./apps/ai.nix
./apps/install_flatpaks.nix ./apps/install_flatpaks.nix
./apps/install_packages.nix ./apps/install_packages.nix
./core/files.nix ./core/files.nix
@@ -894,30 +893,6 @@ This section describes the main system configuration for the computers that I ha
** Apps section ** Apps section
This section describes a way of installing packages, either through nixpkgs orr flatpak. What apps to instal is decided in the files ./assets/conf/apps/packages.conf and flatpaks.conf This section describes a way of installing packages, either through nixpkgs orr flatpak. What apps to instal is decided in the files ./assets/conf/apps/packages.conf and flatpaks.conf
** ai.nix
This module enables and configures the Ollama system service on NixOS, including optional GPU acceleration (CUDA or ROCm).
It ensures the Ollama CLI is available system-wide for interacting with local models.
It automatically pulls and prepares selected coding models (e.g., Qwen2.5-Coder and StarCoder2) at system activation.
#+begin_src nix :tangle configuration/apps/ai.nix :noweb tangle :mkdirp yes
{ config, lib, pkgs, ... }:
{
services.ollama = {
enable = true;
package = pkgs.ollama-vulkan;
loadModels = [
"qwen2.5-coder:7b"
"qwen2.5-coder:32b"
"starcoder2:15b"
];
};
environment.systemPackages = [
pkgs.ollama-vulkan
];
}
#+end_src
** install_packages.nix ** install_packages.nix
#+begin_src nix :tangle configuration/apps/install_packages.nix :noweb tangle :mkdirp yes #+begin_src nix :tangle configuration/apps/install_packages.nix :noweb tangle :mkdirp yes
{ config, lib, pkgs, flakeRoot, inputs, ... }: { config, lib, pkgs, flakeRoot, inputs, ... }:
@@ -1674,6 +1649,88 @@ This module will import all necessities.
} }
#+end_src #+end_src
** AI integrated with ZED
This module enables and configures the Ollama system service on NixOS, including optional GPU acceleration (CUDA or ROCm).
It ensures the Ollama CLI is available system-wide for interacting with local models.
It automatically pulls and prepares selected coding models (e.g., Qwen2.5-Coder and StarCoder2) at system activation.
#+begin_src nix :tangle home/apps/ai.nix :noweb tangle :mkdirp yes
{ config, pkgs, ... }:
{
# Install ZED and Ollama (Vulkan for CPU/AMD, use `ollama` for NVIDIA or `ollama-rocm` for AMD ROCm)
home.packages = [
pkgs.ollama-vulkan # For Vulkan (CPU/AMD). For NVIDIA: pkgs.ollama. For AMD ROCm: pkgs.ollama-rocm
pkgs.zed
];
# Environment variables for ZED and Ollama
home.sessionVariables = {
OLLAMA_HOST = "http://127.0.0.1:11434";
MISTRAL_API_KEY = "CWo91GHwIClzLj6bCLQ69IioSi54PpTZ"; # Replace with your actual Mistral API key
};
# Configure Ollama as a user service (starts with login)
home.services.ollama = {
enable = true;
package = pkgs.ollama-vulkan;
onStart = ''
# Start Ollama server
${pkgs.ollama-vulkan}/bin/ollama serve > /dev/null 2>&1 &
sleep 5 # Wait for server to start
# Pull coding and chat models at startup
${pkgs.ollama-vulkan}/bin/ollama pull codellama:70b # Best for coding
${pkgs.ollama-vulkan}/bin/ollama pull mixtral:8x7b # Best for chat
# To pull additional models, uncomment or add lines below:
# ${pkgs.ollama-vulkan}/bin/ollama pull llama3:8b # General-purpose
# ${pkgs.ollama-vulkan}/bin/ollama pull qwen2.5-coder:7b # Multilingual coding
# ${pkgs.ollama-vulkan}/bin/ollama pull qwen2.5-coder:32b # Larger coding model
# ${pkgs.ollama-vulkan}/bin/ollama pull starcoder2:15b # Alternative for code
'';
};
# Configure ZED to use Ollama and Mistral API
home.file.".config/zed/settings.json".text = lib.mkForce ''
{
"mistral": {
"apiKey": "$MISTRAL_API_KEY", # Uses the environment variable set above
"defaultModel": "mistral-pro" # Default model for Mistral API calls
},
"ollama": {
"endpoint": "$OLLAMA_HOST", # Connects to local Ollama instance
"defaultModel": "codellama:70b" # Default model for Ollama plugin
},
# Add other ZED plugin configurations here if needed
}
'';
# --- Notes ---
# 1. Pulling Additional Models:
# To pull more models later, run:
# ollama pull <model-name>
# Example: ollama pull llama3:8b
# 2. Switching GPU Backends:
# - For NVIDIA: Replace `ollama-vulkan` with `ollama` (uses CUDA)
# - For AMD: Use `ollama-rocm` and ensure ROCm is installed
# 3. ZED Plugin Setup:
# - Install the Ollama and Mistral plugins in ZED via the plugin marketplace
# - The Ollama plugin will use the models pulled above
# - The Mistral plugin will use the MISTRAL_API_KEY for cloud access
# 4. Custom Prompts:
# To add custom prompts for Ollama, create a prompts.json file or
# configure prompts directly in the ZED Ollama plugin settings
# 5. Resource Management:
# Ollama runs as a user service and stops when you log out
# To run Ollama persistently, consider a systemd user service with `systemctl --user`
}
#+end_src
** NCSway ** NCSway
Takes care of notifications Takes care of notifications
#+begin_src nix :tangle home/desktop/ncsway.nix :noweb tangle :mkdirp yes #+begin_src nix :tangle home/desktop/ncsway.nix :noweb tangle :mkdirp yes
+15
View File
@@ -0,0 +1,15 @@
{ config, lib, pkgs, ... }:
{
services.ollama = {
enable = true;
package = pkgs.ollama-vulkan;
loadModels = [
"qwen2.5-coder:7b"
"qwen2.5-coder:32b"
"starcoder2:15b"
];
};
environment.systemPackages = [
pkgs.ollama-vulkan
];
}
@@ -0,0 +1,107 @@
{ config, pkgs, lib, flakeRoot, ... }:
let
moduleName = "install-flatpaks";
flatpakConfPath = flakeRoot + "/assets/conf/apps/flatpaks.conf";
raw = builtins.readFile flatpakConfPath;
# Explicit "\n" so we never accidentally split into characters
rawLines = lib.splitString "\n" raw;
# Guard: if we accidentally split into characters, rawLines length ~= stringLength raw
_guard = assert !(
builtins.stringLength raw > 1 &&
builtins.length rawLines == builtins.stringLength raw
); true;
cleanLine = l:
let
noCR = lib.replaceStrings [ "\r" ] [ "" ] l;
noInlineComment = lib.head (lib.splitString "#" noCR);
in
lib.strings.trim noInlineComment;
entries =
builtins.filter (l: l != "")
(map cleanLine rawLines);
# Flatpak app IDs are reverse-DNS style like org.example.App (at least 2 dots).
# We'll validate and fail early with a clear message.
dotCount = s: builtins.length (lib.splitString "." s) - 1;
isValidId = s:
(dotCount s) >= 2; # matches the error you're seeing: "at least 2 periods"
_validate =
builtins.seq _guard (
builtins.map (id:
if isValidId id then true else
throw ''
${moduleName}: invalid Flatpak ID in flatpaks.conf (needs reverse-DNS with at least 2 dots)
Token : ${builtins.toJSON id}
flatpaks.conf : ${toString flatpakConfPath}
Fix: remove stray tokens/headers, or comment them out with '#'.
''
) entries
);
# Use validated entries
flatpakApps = builtins.seq _validate entries;
syncFlatpaks = pkgs.writeShellScript "sync-flatpaks" ''
set -euo pipefail
# Use the deployed config path (matches environment.etc below)
CONF="/etc/flatpak/flatpaks.conf"
if [[ -f "$CONF" ]]; then
echo "flatpak-sync: using $CONF"
else
echo "flatpak-sync: WARNING: $CONF not found, using embedded list"
fi
if ! flatpak remotes --system --columns=name | grep -qx flathub; then
flatpak remote-add --system --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
fi
desired_apps=(
${lib.concatStringsSep "\n" (map (a: ''"${a}"'') flatpakApps)}
)
for app in "''${desired_apps[@]}"; do
if ! flatpak info --system "$app" >/dev/null 2>&1; then
flatpak install --system -y --noninteractive flathub "$app"
fi
done
'';
in
{
services.flatpak.enable = true;
xdg.portal = {
enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-hyprland
xdg-desktop-portal-gtk
];
};
# Deploy the config file for runtime visibility/debugging
environment.etc."flatpak/flatpaks.conf".source = flatpakConfPath;
systemd.services.flatpak-sync = {
description = "Install Flatpak apps listed in flatpaks.conf";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = syncFlatpaks;
};
restartTriggers = [ flatpakConfPath ];
path = [ pkgs.flatpak pkgs.coreutils pkgs.gnugrep pkgs.gnused ];
};
}
@@ -0,0 +1,53 @@
{ config, lib, pkgs, flakeRoot, inputs, ... }:
let
packagesConfPath = flakeRoot + "/assets/conf/apps/packages.conf";
raw = builtins.readFile packagesConfPath;
# IMPORTANT: explicit "\n" so we never accidentally split into characters
rawLines = lib.splitString "\n" raw;
# Guard: if we accidentally split into characters, rawLines length ~= stringLength raw
_guard = assert !(
builtins.stringLength raw > 1 &&
builtins.length rawLines == builtins.stringLength raw
); true;
cleanLine = l:
let
noCR = lib.replaceStrings [ "\r" ] [ "" ] l;
noInlineComment = lib.head (lib.splitString "#" noCR);
in
lib.strings.trim noInlineComment;
entries =
builtins.filter (l: l != "")
(map cleanLine rawLines);
resolvePkg = name:
let
parts = lib.splitString "." name;
found = lib.attrByPath parts null pkgs;
in
if found == null then
throw ''
install_packages.nix: package not found in pkgs
Token : ${builtins.toJSON name}
packages.conf : ${toString packagesConfPath}
Hint : check the attribute name on search.nixos.org/packages
''
else
found;
packages = builtins.seq _guard (map resolvePkg entries);
zenBrowser =
inputs.zen-browser.packages.${pkgs.stdenv.hostPlatform.system}.default;
in
{
environment.systemPackages =
packages
++ [ zenBrowser ];
}
+27
View File
@@ -0,0 +1,27 @@
{ pkgs, ... } :
{
boot = {
initrd = {
verbose = false; # its a lot of logs. dont need it, unless we do.
kernelModules = [ ]; # no kernel modules on boot
};
extraModulePackages = [ ]; # no extra packages on boot either
kernelPackages = pkgs.linuxPackages_latest; # latest greatest linux kernel
kernelParams = [ "silent" ]; # quiet those logs
consoleLogLevel = 0; # quiten more logs
plymouth.enable = true; # graphical boot animation instead
supportedFilesystems = [ "ntfs" ]; # should see the ntfs (windows)
loader = {
systemd-boot.enable = true; # systemd-boot
systemd-boot.configurationLimit = 10;
efi.canTouchEfiVariables = true; # allow editing efi to edit the boot loader
timeout = 5; # grub timeout to make a selection
};
};
}
+29
View File
@@ -0,0 +1,29 @@
{ pkgs, user, config, ... }:
{
environment.systemPackages = with pkgs; [
zip
unzip
p7zip
usbutils
udiskie
file-roller
];
programs.thunar = {
enable = true;
plugins = with pkgs; [
thunar-archive-plugin
thunar-media-tags-plugin
thunar-volman
thunar-vcs-plugin
];
};
programs.xfconf.enable = true; # to save thunar settings
services = {
gvfs.enable = true; # Mount, trash, and other functionalities
tumbler.enable = true; # Thumbnail support for images
udisks2.enable = true; # Auto mount usb drives
};
}
@@ -0,0 +1,24 @@
{ user, ... } :
let
locale = user.locale;
defaultLocale = "nl_NL.UTF-8";
in
{
# Set your time zone.
time.timeZone = "Europe/Amsterdam";
# Select internationalisation properties.
i18n.defaultLocale = defaultLocale;
i18n.extraLocaleSettings = {
LC_ADDRESS = locale;
LC_IDENTIFICATION = locale;
LC_MEASUREMENT = locale;
LC_MONETARY = locale;
LC_NAME = locale;
LC_NUMERIC = locale;
LC_PAPER = locale;
LC_TELEPHONE = locale;
LC_TIME = defaultLocale;
};
}
@@ -0,0 +1,139 @@
{ config, pkgs, lib, ... }:
let
lightdmConf = builtins.readFile ../../assets/conf/core/lightdm.conf;
lockPng = ../../assets/lock.png;
greeterConfPath = ../../assets/conf/core/lightdm-gtk-greeter.conf;
greeterRaw = builtins.readFile greeterConfPath;
# Extract "key = value" from the greeter conf.
# Returns null if not found.
getIniValue = key:
let
lines = lib.splitString "\n" greeterRaw;
# Captures the value part (group 0) from a single line.
# We match line-by-line because Nix regex does NOT support PCRE flags like (?s).
m =
let
ms = builtins.filter (x: x != null) (map (line:
builtins.match
("^[[:space:]]*" + key + "[[:space:]]*=[[:space:]]*([^#;]+).*$")
line
) lines);
in
if ms == [] then null else builtins.elemAt ms 0;
in
if m == null then null else lib.strings.trim (builtins.elemAt m 0);
# In your greeter.conf these are *package keys*, not theme names.
themePkgKey = getIniValue "theme-name";
iconPkgKey = getIniValue "icon-theme-name";
cursorPkgKey = getIniValue "cursor-theme-name";
cursorSizeStr = getIniValue "cursor-theme-size";
cursorSize =
if cursorSizeStr == null then null
else lib.toInt (lib.strings.trim cursorSizeStr);
# Map package-keys (from greeter.conf) -> { package, name }
#
# IMPORTANT:
# - "name" must be the real theme/icon/cursor NAME as seen under share/themes or share/icons.
# - "package" is the Nixpkgs derivation providing it.
pkgMap = {
catppuccinThemePkg = {
package = pkgs.catppuccin-gtk.override {
accents = [ "blue" ];
variant = "mocha";
size = "standard";
tweaks = [ ];
};
name = "Catppuccin-Mocha-Standard-Blue-Dark";
};
papirus-icon-theme = {
package = pkgs.papirus-icon-theme;
name = "Papirus-Dark";
};
bibata-cursors = {
package = pkgs.bibata-cursors;
name = "Bibata-Modern-Ice";
};
};
pick = key:
if key == null then
throw "lightdm: missing required key in ${toString greeterConfPath}"
else if !(pkgMap ? "${key}") then
throw "lightdm: unknown package key '${key}' in ${toString greeterConfPath}. Known keys: ${lib.concatStringsSep ", " (builtins.attrNames pkgMap)}"
else
pkgMap."${key}";
themeSel = pick themePkgKey;
iconSel = pick iconPkgKey;
cursorSel = pick cursorPkgKey;
# Rewrite greeter.conf so LightDM sees REAL names, not package keys.
# Also force background to lockPng.
greeterFixed =
''
[greeter]
theme-name = ${themeSel.name}
icon-theme-name = ${iconSel.name}
cursor-theme-name = ${cursorSel.name}
${lib.optionalString (cursorSize != null) "cursor-theme-size = ${toString cursorSize}"}
''
+ "\n"
+ greeterRaw;
in
{
services.greetd.enable = false;
services.xserver = {
enable = true;
desktopManager.xterm.enable = false;
displayManager.lightdm = {
enable = true;
background = lockPng;
greeters.gtk = {
enable = true;
theme = {
name = themeSel.name;
package = themeSel.package;
};
iconTheme = {
name = iconSel.name;
package = iconSel.package;
};
cursorTheme = {
name = cursorSel.name;
package = cursorSel.package;
} // lib.optionalAttrs (cursorSize != null) {
size = cursorSize;
};
# This includes your (rewritten) greeter config.
extraConfig = greeterFixed;
};
extraConfig = lightdmConf;
};
};
programs.hyprland.enable = true;
# Optional: make them available system-wide as well
environment.systemPackages = [
themeSel.package
iconSel.package
cursorSel.package
];
}
@@ -0,0 +1,14 @@
{ pkgs, user, ... } :
{
environment.systemPackages = with pkgs; [
tuigreet
];
services.greetd = {
enable = true;
settings = {
default_session = {
command = pkgs.lib.mkForce "${pkgs.tuigreet}/bin/tuigreet --remember --time --time-format '%I:%M %p | %a %h | %F'";
};
};
};
}
@@ -0,0 +1,21 @@
{ pkgs, lib, ... }:
{
networking = {
useDHCP = lib.mkDefault true;
networkmanager.enable = true;
networkmanager.wifi.backend = "iwd";
wireless.iwd.enable = true;
wireless.userControlled.enable = true;
firewall = {
enable = true;
# KDE Connect: discovery + encrypted connections
allowedTCPPortRanges = [
{ from = 1714; to = 1764; }
];
allowedUDPPortRanges = [
{ from = 1714; to = 1764; }
];
};
};
environment.systemPackages = with pkgs; [ impala ];
}
@@ -0,0 +1,38 @@
{ pkgs, user, ... } :
{
nix.settings = {
# enable flakes
experimental-features = ["nix-command" "flakes"];
# add a cache that speed up new applications by downloading binaries
# from the trusted cache instead of compiling from sourcer
substituters = [
"https://nix-community.cachix.org"
];
# trust the cache public key
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
# allow proprietary software on this machine. I'm not a purist.
nixpkgs.config.allowUnfree = true;
# unityhub depends on this... for now
nixpkgs.config.permittedInsecurePackages = [ "libxml2-2.13.8" ];
# this declares how often old configurations are cleared up.
# i cleanup anything older than a week, every week.
nix.gc = {
automatic = true;
options = "--delete-older-than 7d";
dates = "weekly";
};
programs = {
# command line utility that makes applying changes easy and pretty
nh = {
enable = true;
flake = "/home/${user.username}/system";
};
};
}
+45
View File
@@ -0,0 +1,45 @@
{ pkgs, user, ... } :
{
imports = [
./apps/install_flatpaks.nix
./apps/install_packages.nix
./core/files.nix
./core/locale.nix
./core/networking.nix
./core/nix-settings.nix
#./core/login-tuigreeter.nix
./core/login-lightdm.nix
./desktop/audio.nix
./desktop/hyprland.nix
./dev/terminal.nix
./core/boot.nix
./services/services.nix
];
users.users.${user.username} = {
isNormalUser = true;
description = "henrov";
extraGroups = [
"networkmanager" # allow editing network connections
"wheel" # can do sudo
"scanner" # access to the network scanner
"lp" # access to the printer
];
};
fonts.packages = with pkgs; [
aporetic
nerd-fonts.iosevka
];
# enable the catppuccin theme for everything with mocha + blue accents
catppuccin.enable = true;
catppuccin.flavor = "mocha";
catppuccin.accent = "blue";
system.stateVersion = user.stateVersion;
}
@@ -0,0 +1,70 @@
{ config, pkgs, lib, ... }:
{
environment.systemPackages = with pkgs; [
pipewire
wireplumber
alsa-utils
pulseaudio
pamixer
pavucontrol
];
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
jack.enable = true;
wireplumber.enable = true;
};
security.rtkit.enable = true;
# Helps on many laptops (Intel SOF etc.)
hardware.enableRedistributableFirmware = true;
# Prefer analog over HDMI/DP in a machine-agnostic way
services.pipewire.wireplumber.extraConfig."51-audio-priorities" = {
"monitor.alsa.rules" = [
# De-prioritize HDMI / DisplayPort sinks
{
matches = [
{ "node.name" = "~alsa_output\\..*HDMI.*"; }
{ "node.name" = "~alsa_output\\..*DisplayPort.*"; }
];
actions.update-props = {
"priority.session" = 100;
"priority.driver" = 100;
};
}
# Prefer analog sinks (speakers/headphones)
{
matches = [
{ "node.name" = "~alsa_output\\..*analog.*"; }
{ "node.name" = "~alsa_output\\..*Headphones.*"; }
{ "node.name" = "~alsa_output\\..*Speaker.*"; }
];
actions.update-props = {
"priority.session" = 2000;
"priority.driver" = 2000;
};
}
];
};
# Optional: clear "sticky" user-selected defaults so priority rules win
systemd.user.services.wireplumber-clear-default-nodes = {
description = "Clear WirePlumber saved default nodes (avoid HDMI becoming sticky)";
after = [ "wireplumber.service" ];
partOf = [ "wireplumber.service" ];
wantedBy = [ "default.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.coreutils}/bin/rm -f %h/.local/state/wireplumber/default-nodes";
};
};
}
@@ -0,0 +1,58 @@
{ pkgs, ... }:
{
nix.settings = {
substituters = [ "https://hyprland.cachix.org" ];
trusted-public-keys = [
"hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="
];
};
services.dbus.enable = true;
security.polkit.enable = true;
services.flatpak.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
wireplumber.enable = true;
};
services.gvfs.enable = true;
xdg.portal = {
enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-hyprland
xdg-desktop-portal-gtk
];
config.common.default = [ "hyprland" "gtk" ];
};
environment.systemPackages = with pkgs; [
walker
uwsm
hyprland-qtutils
hyprpolkitagent
grimblast
];
programs = {
uwsm.enable = true;
uwsm.waylandCompositors.hyprland = {
prettyName = "Hyprland";
comment = "Hyprland compositor managed by UWSM";
binPath = "/run/current-system/sw/bin/Hyprland";
};
hyprland = {
withUWSM = true;
enable = true;
xwayland.enable = true;
};
};
environment.sessionVariables = {
XDG_SESSION_TYPE = "wayland";
XDG_CURRENT_DESKTOP = "Hyprland";
XDG_SESSION_DESKTOP = "Hyprland";
NIXOS_OZONE_WL = "1";
XCURSOR_SIZE = "24";
};
security.pam.services.hyprlock = { };
# Optional; GNOME-specific (keep only if you really use gnome-keyring integration)
security.pam.services.gdm.enableGnomeKeyring = true;
}
@@ -0,0 +1,8 @@
{ pkgs, user, ... }:
{
console.useXkbConfig = true;
users.users.${user.username}.shell = pkgs.zsh;
programs.zsh.enable = true;
environment.shells = [ pkgs.zsh ];
environment.pathsToLink = [ "/share/zsh" ];
}
@@ -0,0 +1,23 @@
{ user, ...} :
{
services = {
blueman.enable = true; # bluetooth manager
fwupd.enable = true; # firmware updating service
fstrim.enable = true; # ssd maintenance service
thermald.enable = true; # thermal regulation service
printing.enable = true; # printing services, cups
gnome.gnome-keyring.enable = true; # keyring
flatpak.enable = true; # allow installing things from flatpaks
#flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# printer discovery
avahi = {
enable = true;
nssmdns4 = true;
openFirewall = true;
};
};
virtualisation.docker.enable = true; # enable docker
users.users.${user.username}.extraGroups = [ "docker" ]; # add self to docker user group
}
+138
View File
@@ -0,0 +1,138 @@
{ config, pkgs, ... }:
{
# Install ZED and Ollama (Vulkan for CPU/AMD, use `ollama` for NVIDIA or `ollama-rocm` for AMD ROCm)
home.packages = [
pkgs.ollama-vulkan # For Vulkan (CPU/AMD). For NVIDIA: pkgs.ollama. For AMD ROCm: pkgs.ollama-rocm
pkgs.zed
];
# Environment variables for ZED and Ollama
home.sessionVariables = {
OLLAMA_HOST = "http://127.0.0.1:11434";
MISTRAL_API_KEY = "CWo91GHwIClzLj6bCLQ69IioSi54PpTZ"; # Replace with your actual Mistral API key
};
# Configure Ollama as a user service (starts with login)
home.services.ollama = {
enable = true;
package = pkgs.ollama-vulkan;
onStart = ''
# Start Ollama server
${pkgs.ollama-vulkan}/bin/ollama serve > /dev/null 2>&1 &
sleep 5 # Wait for server to start
# Pull coding and chat models at startup
${pkgs.ollama-vulkan}/bin/ollama pull codellama:70b # Best for coding
${pkgs.ollama-vulkan}/bin/ollama pull mixtral:8x7b # Best for chat
# To pull additional models, uncomment or add lines below:
# ${pkgs.ollama-vulkan}/bin/ollama pull llama3:8b # General-purpose
# ${pkgs.ollama-vulkan}/bin/ollama pull qwen2.5-coder:7b # Multilingual coding
# ${pkgs.ollama-vulkan}/bin/ollama pull qwen2.5-coder:32b # Larger coding model
# ${pkgs.ollama-vulkan}/bin/ollama pull starcoder2:15b # Alternative for code
'';
};
# Configure ZED to use Ollama and Mistral API
home.file.".config/zed/settings.json".text = lib.mkForce ''
{
"mistral": {
"apiKey": "$MISTRAL_API_KEY", # Uses the environment variable set above
"defaultModel": "mistral-pro" # Default model for Mistral API calls
},
"ollama": {
"endpoint": "$OLLAMA_HOST", # Connects to local Ollama instance
"defaultModel": "codellama:70b" # Default model for Ollama plugin
},
# Add other ZED plugin configurations here if needed
}
'';
# --- Notes ---
# 1. Pulling Additional Models:
# To pull more models later, run:
# ollama pull <model-name>
# Example: ollama pull llama3:8b
# 2. Switching GPU Backends:
# - For NVIDIA: Replace `ollama-vulkan` with `ollama` (uses CUDA)
# - For AMD: Use `ollama-rocm` and ensure ROCm is installed
# 3. ZED Plugin Setup:
# - Install the Ollama and Mistral plugins in ZED via the plugin marketplace
# - The Ollama plugin will use the models pulled above
# - The Mistral plugin will use the MISTRAL_API_KEY for cloud access
# 4. Custom Prompts:
# To add custom prompts for Ollama, create a prompts.json file or
# configure prompts directly in the ZED Ollama plugin settings
# 5. Resource Management:
# Ollama runs as a user service and stops when you log out
# To run Ollama persistently, consider a systemd user service with `systemctl --user`
}
{ config, lib, pkgs, ... }:
let
# Continue gebruikt tegenwoordig bij voorkeur config.yaml; config.json bestaat nog
# maar is “deprecated” in de docs. We schrijven hier bewust config.json omdat jij dat vroeg.
continueConfigJson = builtins.toJSON {
models = [
{
title = "Qwen2.5-Coder 7B";
provider = "ollama";
model = "qwen2.5-coder:7b";
apiBase = "http://localhost:11434";
}
{
title = "Qwen2.5-Coder 32B";
provider = "ollama";
model = "qwen2.5-coder:32b";
apiBase = "http://localhost:11434";
}
{
title = "StarCoder2 15B";
provider = "ollama";
model = "starcoder2:15b";
apiBase = "http://localhost:11434";
}
];
# Tab-autocomplete model (pas aan naar smaak/VRAM)
tabAutocompleteModel = {
title = "Qwen2.5-Coder 7B";
provider = "ollama";
model = "qwen2.5-coder:7b";
apiBase = "http://localhost:11434";
};
};
in
{
programs.zed-editor = {
enable = true;
# Zed-extensies (taal/LS/etc). "Continue" bestaat (nog) niet als Zed-extensie.
# Dit is de officiële HM interface voor Zed extensions.
extensions = [
"nix"
"toml"
"rust"
"org-mode"
];
# Zed AI: Ollama als provider
# Zed kan modellen auto-discoveren die jij met Ollama gepulld hebt.
userSettings = {
language_models = {
ollama = {
api_url = "http://localhost:11434";
auto_discover = true;
# Optioneel: zet een grotere context voor alle Ollama modellen
# (Zed stuurt dit als `num_ctx` naar Ollama)
context_window = 8192;
};
};
};
};
# Continue config.json neerzetten (voor Continue in VS Code / JetBrains)
# Pad: ~/.config/continue/config.json
xdg.configFile."continue/config.json".text = continueConfigJson;
}
+9
View File
@@ -0,0 +1,9 @@
{ config, pkgs, lib, ... }:
{
xdg.mimeApps.enable = true;
xdg.mimeApps.defaultApplications = {
"x-scheme-handler/http" = [ "app.zen_browser.zen.desktop" ];
"x-scheme-handler/https" = [ "app.zen_browser.zen.desktop" ];
"text/html" = [ "app.zen_browser.zen.desktop" ];
};
}
+22
View File
@@ -0,0 +1,22 @@
{ pkgs, ...}:
{
gtk = {
enable = true;
colorScheme = "dark";
theme = {
name = "Catppuccin-GTK-Grey-Dark-Compact";
package = (pkgs.magnetic-catppuccin-gtk.override {
accent = [ "grey" ];
shade = "dark";
tweaks = [ "black" ];
size = "compact";
});
};
iconTheme.name = "Papirus-Dark";
};
catppuccin.enable = true;
catppuccin.flavor = "mocha";
catppuccin.accent = "blue";
catppuccin.gtk.icon.enable = true;
catppuccin.cursors.enable = true;
}
+35
View File
@@ -0,0 +1,35 @@
{ pkgs, user, ... } :
{
imports = [
./apps/ai.nix
#./apps/default-apps.nix
./apps/theme.nix
./desktop/hypridle.nix
./desktop/hyprland.nix
./desktop/hyprexpo.nix
./desktop/hyprlock.nix
./desktop/hyprscrolling.nix
./desktop/hyprshell.nix
./desktop/ncsway.nix
./desktop/powermenu.nix
#./desktop/animated_wallpaper.nix
#./desktop/rotating_wallpaper.nix
./desktop/workspace_wallpaper.nix
./desktop/waybar.nix
./desktop/walker.nix
./dev/dev.nix
./dev/kitty.nix
./dev/shells.nix
./dev/starship.nix
./dev/zsh.nix
./dev/emacs
];
home.username = "${user.username}";
home.homeDirectory = pkgs.lib.mkDefault "/home/${user.username}";
home.stateVersion = user.stateVersion;
programs.home-manager.enable = true;
}
@@ -0,0 +1,50 @@
{ config, pkgs, lib, flakeRoot, ... }:
let
repoWallpaperDir = flakeRoot + "/assets/conf/desktop/wallpaper";
userRelRoot = "nixos_conf/wallpaperstuff";
userAbsRoot = "${config.home.homeDirectory}/${userRelRoot}";
# The video file you want as wallpaper (must exist under the synced dir)
userVideoPath = "${userAbsRoot}/videos/myWallpaper.mp4";
# (keep your existing approach: sync the repo wallpaper dir to the user dir)
repoWallpapersOnly = lib.cleanSourceWith {
src = repoWallpaperDir;
filter = path: type: true;
};
in
{
home.packages = [
pkgs.mpvpaper
pkgs.mpv
];
# Sync repo wallpapers (including videos/) into ~/nixos_conf/wallpaperstuff
home.file."${userRelRoot}" = {
source = repoWallpapersOnly;
recursive = true;
};
systemd.user.services.mpvpaper-wallpaper = {
Unit = {
Description = "Video wallpaper (mpvpaper)";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
# -p auto-pause saves resources when the wallpaper surface is hidden.
# '*' applies to all outputs.
# Stretch-to-fill (cover) behavior:
# --panscan=1.0 fills the entire output by cropping (no letterboxing).
# If you literally want distortion-stretch (ignore aspect ratio), use --keepaspect=no instead.
ExecStart = ''
${pkgs.mpvpaper}/bin/mpvpaper \
-p \
-o "no-audio --loop-file=inf --no-terminal --really-quiet --panscan=1.0 --keepaspect=yes" \
'*' "${userVideoPath}"
'';
Restart = "on-failure";
RestartSec = 1;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
}
+28
View File
@@ -0,0 +1,28 @@
{ config, lib, pkgs, ... }:
{
wayland.windowManager.hyprland = {
# Load the Hyprexpo plugin (from nixpkgs)
plugins = [
pkgs.hyprlandPlugins.hyprexpo
];
# Append plugin config + keybind after your existing hyprland.conf
extraConfig = lib.mkAfter ''
############################
# Hyprexpo (workspace/window overview)
############################
# Basic plugin config (tweak as you like)
plugin {
hyprexpo {
columns = 3
gaps_in = 5
gaps_out = 20
# Optional; comment out if you don't want it
# workspace_method = center current
}
}
'';
};
}
+8
View File
@@ -0,0 +1,8 @@
{ config, lib, pkgs, flakeRoot, ... }:
let
hypridleConf = flakeRoot + "/assets/conf/desktop/hypr/hypridle.conf";
in
{
home.packages = [ pkgs.hypridle ];
xdg.configFile."hypr/hypridle.conf".source = hypridleConf;
}
+40
View File
@@ -0,0 +1,40 @@
{ config, lib, pkgs, flakeRoot, ... }:
let
hyprConf = flakeRoot + "/assets/conf/desktop/hypr/hyprland.conf";
bindingsConf = flakeRoot + "/assets/conf/desktop/hypr/bindings.conf";
in
{
wayland.windowManager.hyprland = {
enable = true;
# Load base config + bindings from repo files
extraConfig =
(builtins.readFile hyprConf)
+ "\n\n# --- Repo keybindings ---\n"
+ (builtins.readFile bindingsConf)
+ "\n";
settings = {
windowrule = [
"match:class nm-connection-editor, float 1, center 1, size 900 700"
];
};
};
xdg.configFile."hypr/scripts/lid-lock.sh" = {
source = flakeRoot + "/assets/conf/desktop/hypr/scripts/lid-lock.sh";
executable = true;
};
xdg.portal = {
enable = true;
extraPortals = with pkgs; [
xdg-desktop-portal-gtk
xdg-desktop-portal-hyprland
];
# GTK als algemene backend (OpenURI is daar betrouwbaar)
config.common.default = [ "gtk" ];
# Hyprland alleen voor screensharing / remote desktop
config.hyprland = {
"org.freedesktop.impl.portal.Screencast" = [ "hyprland" ];
"org.freedesktop.impl.portal.RemoteDesktop" = [ "hyprland" ];
};
};
}
+10
View File
@@ -0,0 +1,10 @@
{config, lib, pkgs, flakeRoot, ... }:
let
lockPngSrc = flakeRoot + "/assets/lock.png";
hyprlockConf = flakeRoot + "/assets/conf/desktop/hypr/hyprlock.conf";
in
{
home.packages = [ pkgs.hyprlock ];
xdg.configFile."hypr/lock.png".source = lockPngSrc;
xdg.configFile."hypr/hyprlock.conf".source = hyprlockConf;
}
@@ -0,0 +1,47 @@
{ config, lib, pkgs, flakeRoot,...}:
let
# Hyprscrolling drop-in config (repo -> ~/.config)
repoConf = flakeRoot + "/assets/conf/desktop/hypr/hyprscrolling.conf";
targetRel = "hypr/conf.d/90-hyprscrolling.conf";
# Overflow indicator script (repo -> ~/.config)
repoOverflowScript = flakeRoot + "/assets/conf/desktop/hypr/scripts/hyprscroll-overflow.sh";
targetOverflowRel = "hypr/scripts/hyprscroll-overflow.sh";
# Adapt columnsize to monitor
repoPerMonitorScript = flakeRoot + "/assets/conf/desktop/hypr/scripts/hyprscrolling-per-monitor.sh";
targetPerMonitor = "hypr/scripts/hyprscrolling-per-monitor.sh";
# Facilitate switching between scrolling and dwindle
repoSwitchScript =
flakeRoot + "/assets/conf/desktop/hypr/scripts/toggle-layout-scrolling-dwindle.sh";
targetSwitchScript = "hypr/scripts/toggle-layout-scrolling-dwindle.sh";
in
{
# Ensure deps for the script exist at runtime
# (hyprctl comes with Hyprland; jq is often not installed by default)
home.packages = with pkgs; [
jq
];
wayland.windowManager.hyprland = {
enable = true;
plugins = [
pkgs.hyprlandPlugins.hyprscrolling
];
extraConfig = ''
source = ~/.config/${targetRel}
'';
};
# Copy repo configs/scripts into ~/.config
xdg.configFile."${targetRel}".source = repoConf;
xdg.configFile."${targetOverflowRel}" = {
source = repoOverflowScript;
executable = true; # makes it chmod +x
};
xdg.configFile."${targetPerMonitor}" = {
source = repoPerMonitorScript;
executable = true; # makes it chmod +x
};
xdg.configFile."${targetSwitchScript}" = {
source = repoSwitchScript;
executable = true; # makes it chmod +x
};
}
+30
View File
@@ -0,0 +1,30 @@
# home/desktop/hyprshell.nix (Home-Manager module)
{ config, pkgs, lib, flakeRoot, ... }:
let
repoDir = flakeRoot + "/assets/conf/desktop/hypr/hyprshell";
cfgRon = repoDir + "/config.ron";
cssFile = repoDir + "/styles.css";
in
{
xdg.enable = true;
home.packages = [ pkgs.hyprshell ];
# Link repo -> ~/.config/hyprshell/...
xdg.configFile."hyprshell/config.ron".source = cfgRon;
xdg.configFile."hyprshell/styles.css".source = cssFile;
# Autostart (systemd user service)
systemd.user.services.hyprshell = {
Unit = {
Description = "Hyprshell (window switcher / launcher)";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.hyprshell}/bin/hyprshell";
Restart = "on-failure";
RestartSec = 1;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
}
+28
View File
@@ -0,0 +1,28 @@
{ config, pkgs, lib, flakeRoot, ... }:
let
repoConf = flakeRoot + "/assets/conf/desktop/notifications/swaync/config.json";
repoStyle = flakeRoot + "/assets/conf/desktop/notifications/swaync/style.css";
in
{
home.packages = [
pkgs.swaynotificationcenter
pkgs.libnotify
];
# Ensure config directory exists in ~/.config
xdg.configFile."swaync/config.json".source = repoConf;
xdg.configFile."swaync/style.css".source = repoStyle;
# Start swaync automatically (systemd user service)
systemd.user.services.swaync = {
Unit = {
Description = "Sway Notification Center";
PartOf = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.swaynotificationcenter}/bin/swaync";
Restart = "always";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
}
+13
View File
@@ -0,0 +1,13 @@
{ config, lib, pkgs, flakeRoot, ... }:
let
repoScript =
flakeRoot + "/assets/conf/desktop/hypr/scripts/powermenu.sh";
targetRel = "hypr/scripts/powermenu.sh";
in
{
# Ensure script exists in ~/.config/hypr/scripts/
xdg.configFile."${targetRel}" = {
source = repoScript;
executable = true;
};
}
@@ -0,0 +1,42 @@
#+begin_src nix :tangle home/desktop/wallpaper.nix :noweb tangle :mkdirp yes
{ config, pkgs, lib, flakeRoot, ... }:
let
repoWallpaperDir = flakeRoot + "/assets/conf/desktop/wallpaper";
repoWallpaperConf = flakeRoot + "/assets/conf/desktop/wallpaper/wallpaper.conf";
userRelRoot = "nixos_conf/wallpaperstuff";
userAbsRoot = "${config.home.homeDirectory}/${userRelRoot}";
userConfPath = "${userAbsRoot}/wallpaper.conf";
# Exclude wallpaper.conf so HM does NOT manage it (avoids backup collisions)
repoWallpapersOnly = lib.cleanSourceWith {
src = repoWallpaperDir;
filter = path: type:
(builtins.baseNameOf path) != "wallpaper.conf";
};
in
{
home.packages = [ pkgs.wpaperd ];
# Sync everything *except* wallpaper.conf into ~/nixos_conf/wallpaperstuff
home.file."${userRelRoot}" = {
source = repoWallpapersOnly;
recursive = true;
};
# Now safely overwrite the config every activation (no HM collision)
home.activation.wallpaperConfForce =
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
mkdir -p "${userAbsRoot}"
install -m 0644 "${repoWallpaperConf}" "${userConfPath}"
'';
systemd.user.services.wpaperd = {
Unit = {
Description = "wpaperd wallpaper daemon";
After = [ "default.target" ];
};
Service = {
Type = "simple";
ExecStart = "${pkgs.wpaperd}/bin/wpaperd --config ${userConfPath}";
Restart = "on-failure";
RestartSec = 1;
};
Install.WantedBy = [ "default.target" ];
;
+26
View File
@@ -0,0 +1,26 @@
{ config, pkgs, lib, flakeRoot, inputs ? null, ... }:
let
walkerPkg =
if inputs != null && inputs ? walker
then inputs.walker.packages.${pkgs.system}.default
else pkgs.walker;
elephantPkg =
if inputs != null && inputs ? elephant
then inputs.elephant.packages.${pkgs.system}.default
else pkgs.elephant;
sessionTarget = "graphical-session.target";
# All theme files now live here
repoThemesDir = flakeRoot + "/assets/conf/desktop/walker";
in
{
xdg.enable = true;
home.packages = [ walkerPkg elephantPkg ];
# ~/.config/walker/themes/*
xdg.configFile."walker/themes/frosted/default.css".source = repoThemesDir + "/themes/frosted/default.css";
xdg.configFile."walker/themes/frosted/style.css".source = repoThemesDir + "/themes/frosted/style.css";
xdg.configFile."walker/config.toml".source = repoThemesDir + "/config.toml";
# xdg.configFile."walker/themes/default.html".source = repoThemesDir + "/default.html";
# (services unchanged)
systemd.user.services.elephant = { /* ... your existing service ... */ };
systemd.user.services.walker = { /* ... your existing service ... */ };
}
+21
View File
@@ -0,0 +1,21 @@
{ config, lib, pkgs, flakeRoot, ... }:
let
repoWaybarDir = flakeRoot + "/assets/conf/desktop/waybar";
in
{
programs.waybar.enable = true;
# Ensure config matches repo (HM-managed symlink, not user-editable)
xdg.configFile."waybar/config" = {
source = repoWaybarDir + "/config.jsonc";
force = true;
};
# Override HM's internally-generated waybar-style.css derivation
# and use your repo file instead.
xdg.configFile."waybar/style.css" = {
source = lib.mkForce (repoWaybarDir + "/style.css");
force = true;
};
# Prevent HM from also trying to generate style content via programs.waybar.style
# (not strictly required once mkForce is in place, but keeps intent clear)
programs.waybar.style = "";
}
@@ -0,0 +1,203 @@
{ config, lib, pkgs, flakeRoot, ... }:
let
# Where your numbered wallpapers live (1.*, 2.*, ... 9.*)
userRelRoot = "nixos_conf/wallpaperstuff";
userAbsRoot = "${config.home.homeDirectory}/${userRelRoot}";
picturesDir = "${userAbsRoot}/pictures";
# (Optional) still sync your repo wallpapers/scripts into ~/nixos_conf/wallpaperstuff
repoWallpaperDir = flakeRoot + "/assets/conf/desktop/wallpaper";
repoWallpapersOnly = lib.cleanSourceWith {
src = repoWallpaperDir;
filter = path: type: true;
};
daemonRel = "hypr/scripts/hyprpaper-ws-daemon.sh";
setRel = "hypr/scripts/set-wallpaper.sh";
in
{
home.packages = [
pkgs.hyprpaper
pkgs.socat
pkgs.jq
pkgs.findutils
pkgs.coreutils
pkgs.gnused
pkgs.gawk
];
# Keep your existing "sync wallpapers into a writable dir" pattern
home.file."${userRelRoot}" = {
source = repoWallpapersOnly;
recursive = true;
};
# Hyprpaper config (hyprpaper reads this; it does NOT need to write it)
# `ipc = true` enables `hyprctl hyprpaper ...` commands. :contentReference[oaicite:0]{index=0}
xdg.configFile."hypr/hyprpaper.conf".text = ''
ipc = true
splash = false
'';
# Workspace wallpaper daemon: listens to socket2, applies w-<id>=... mapping
# Uses workspacev2 to get numeric workspace id. :contentReference[oaicite:1]{index=1}
xdg.configFile."${daemonRel}" = {
executable = true;
text = ''
#!/usr/bin/env bash
set -euo pipefail
: "''${XDG_RUNTIME_DIR:?XDG_RUNTIME_DIR not set}"
: "''${HYPRLAND_INSTANCE_SIGNATURE:?HYPRLAND_INSTANCE_SIGNATURE not set}"
SOCK="''${XDG_RUNTIME_DIR}/hypr/''${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock"
[[ -S "$SOCK" ]] || { echo "Hyprland socket not found: $SOCK" >&2; exit 1; }
PICTURES_DIR="''${1:-${picturesDir}}"
FIT_MODE="fill" # hyprpaper fit_mode: contain|cover|tile|fill :contentReference[oaicite:2]{index=2}
HYPR_DIR="''${XDG_CONFIG_HOME:-$HOME/.config}/hypr"
MAP_ROOT="''${HYPR_DIR}/hyprpaper/config"
focused_monitor() {
hyprctl -j monitors | jq -r '.[] | select(.focused==true) | .name' | head -n 1
}
map_file_for_monitor() {
local mon="''${1}"
echo "''${MAP_ROOT}/''${mon}/defaults.conf"
}
ensure_map_file() {
local mon="''${1}"
local f
f="$(map_file_for_monitor "''${mon}")"
mkdir -p "$(dirname "''${f}")"
if [[ ! -f "''${f}" ]]; then
# Seed with 1..9 from picturesDir if present, else empty entries
{
for i in 1 2 3 4 5 6 7 8 9; do
seed="$(ls -1 "''${PICTURES_DIR}/''${i}."* 2>/dev/null | head -n 1 || true)"
echo "w-''${i}= ''${seed}"
done
} > "''${f}"
fi
echo "''${f}"
}
get_wall_for_ws() {
local mon="''${1}"
local wsid="''${2}"
local f key val
f="$(ensure_map_file "''${mon}")"
key="w-''${wsid}"
# accept "w-1=/path" or "w-1= /path"
val="$(awk -F= -v k="''${key}" '$1==k {sub(/^[[:space:]]+/, "", $2); print $2; exit}' "''${f}" || true)"
echo "''${val}"
}
apply_wallpaper() {
local mon="''${1}"
local wsid="''${2}"
local file
file="$(get_wall_for_ws "''${mon}" "''${wsid}")"
[[ -n "''${file}" ]] || return 0
[[ -f "''${file}" ]] || return 0
# Apply via IPC
# hyprpaper wallpaper { monitor path fit_mode } model is per monitor. :contentReference[oaicite:3]{index=3}
hyprctl hyprpaper wallpaper "''${mon}, ''${file}, ''${FIT_MODE}" >/dev/null
}
# Initial apply on startup
mon="$(focused_monitor || true)"
wsid="$(hyprctl -j activeworkspace | jq -r '.id' | head -n 1 || true)"
[[ -n "''${mon}" && -n "''${wsid}" ]] && apply_wallpaper "''${mon}" "''${wsid}"
handle() {
local line="''${1}"
case "''${line}" in
workspacev2* )
# workspacev2>>ID,NAME :contentReference[oaicite:4]{index=4}
local payload wsid
payload="''${line#*>>}"
wsid="''${payload%%,*}"
mon="$(focused_monitor || true)"
[[ -n "''${mon}" && -n "''${wsid}" ]] && apply_wallpaper "''${mon}" "''${wsid}"
;;
focusedmon* )
# focusedmon>>MON,WORKSPACENAME :contentReference[oaicite:5]{index=5}
# When monitor focus changes, re-apply for the active workspace id.
mon="$(focused_monitor || true)"
wsid="$(hyprctl -j activeworkspace | jq -r '.id' | head -n 1 || true)"
[[ -n "''${mon}" && -n "''${wsid}" ]] && apply_wallpaper "''${mon}" "''${wsid}"
;;
esac
}
socat -U - UNIX-CONNECT:"''${SOCK}" | while read -r line; do
handle "''${line}" || true
done
'';
};
# CLI setter in the style of your inspiration script.
# Usage: set-wallpaper.sh <workspace_id> <monitor> [wallpaper]
xdg.configFile."${setRel}" = {
executable = true;
text = ''
#!/usr/bin/env bash
set -euo pipefail
HYPR_DIR="''${XDG_CONFIG_HOME:-$HOME/.config}/hypr"
MAP_ROOT="''${HYPR_DIR}/hyprpaper/config"
usage() {
echo "Usage: set-wallpaper.sh <workspace_id> <monitor> [wallpaper_path]"
}
wsid="''${1:-}"
mon="''${2:-}"
wp="''${3:-}"
[[ -n "''${wsid}" ]] || { usage; exit 1; }
[[ -n "''${mon}" ]] || { usage; exit 1; }
cfg="''${MAP_ROOT}/''${mon}/defaults.conf"
mkdir -p "$(dirname "''${cfg}")"
[[ -f "''${cfg}" ]] || touch "''${cfg}"
if [[ -z "''${wp}" ]]; then
# Random pick from your defaults folder if you want; adjust path if needed:
wp="$(find "$HOME/.config/wallpapers/defaults" -type f 2>/dev/null | shuf -n 1 || true)"
[[ -n "''${wp}" ]] || { echo "No wallpaper found (random). Provide a path as arg 3."; exit 1; }
fi
# Ensure key exists; if not, append it
key="w-''${wsid}"
if ! grep -q "^''${key}=" "''${cfg}"; then
echo "''${key}=" >> "''${cfg}"
fi
# Set mapping
${pkgs.gnused}/bin/sed -i "s|^''${key}=.*|''${key}= ''${wp}|g" "''${cfg}"
# If this monitor is currently showing that workspace id, apply immediately
curws="$(hyprctl -j monitors | jq -r --arg m "''${mon}" '.[] | select(.name==$m) | .activeWorkspace.id' | head -n 1 || true)"
if [[ "''${curws}" == "''${wsid}" ]]; then
hyprctl hyprpaper wallpaper "''${mon}, ''${wp}, fill" >/dev/null
fi
'';
};
# Services
systemd.user.services.hyprpaper = {
Unit = {
Description = "hyprpaper wallpaper daemon";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" ];
};
Service = {
ExecStart = "${pkgs.hyprpaper}/bin/hyprpaper";
Restart = "on-failure";
RestartSec = 1;
};
Install = { WantedBy = [ "graphical-session.target" ]; };
};
systemd.user.services.hyprpaper-ws-daemon = {
Unit = {
Description = "Workspace->wallpaper mapping daemon (hyprpaper + socket2)";
PartOf = [ "graphical-session.target" ];
After = [ "graphical-session.target" "hyprpaper.service" ];
};
Service = {
ExecStart = "${pkgs.bash}/bin/bash ${config.xdg.configHome}/${daemonRel} ${picturesDir}";
Restart = "on-failure";
RestartSec = 1;
};
Install = { WantedBy = [ "graphical-session.target" ]; };
};
}
+12
View File
@@ -0,0 +1,12 @@
{ config, pkgs, lib, flakeRoot, ... }:
let
repoAlacrittyConf = flakeRoot + "/assets/conf/dev/alacritty.toml";
in
{
xdg.enable = true;
programs.alacritty.enable = true;
# Override the config generated by programs.alacritty
xdg.configFile."alacritty/alacritty.toml".source = lib.mkForce repoAlacrittyConf;
catppuccin.alacritty.enable = true;
catppuccin.alacritty.flavor = "mocha";
}
+42
View File
@@ -0,0 +1,42 @@
{ config, pkgs, lib, ... }:
{
programs = {
vscode.enable = true;
vim.enable = true;
ripgrep.enable = true;
btop.enable = true;
fzf = {
enable = true;
enableZshIntegration = true;
enableBashIntegration = true;
};
zoxide = {
enable = true;
enableZshIntegration = true;
enableBashIntegration = true;
};
eza = {
enable = true;
enableZshIntegration = true;
enableBashIntegration = true;
};
direnv = {
enable = true;
enableZshIntegration = true;
enableBashIntegration = true;
nix-direnv.enable = true;
};
# Zsh-specific config belongs here
zsh = {
# for emacs-eat package
initContent = lib.mkOrder 1200 ''
[ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \
source "$EAT_SHELL_INTEGRATION_DIR/zsh"
'';
};
git = {
enable = true;
lfs.enable = true;
};
};
}
+67
View File
@@ -0,0 +1,67 @@
{ pkgs, ... }:
{
programs.emacs = {
enable = true;
# install with tree sitter enabled
package = (pkgs.emacs-pgtk.override { withTreeSitter = true; });
extraPackages = epkgs: [
# also install all tree sitter grammars
epkgs.manualPackages.treesit-grammars.with-all-grammars
epkgs.nerd-icons # nerd fonts support
epkgs.doom-modeline # model line
epkgs.diminish # hides modes from modeline
epkgs.eldoc # doc support
epkgs.pulsar # pulses the cursor when jumping about
epkgs.which-key # help porcelain
epkgs.expreg # expand region
epkgs.vundo # undo tree
epkgs.puni # structured editing
epkgs.avy # jumping utility
epkgs.consult # emacs right click
epkgs.vertico # minibuffer completion
epkgs.marginalia # annotations for completions
epkgs.crux # utilities
epkgs.magit # git porcelain
epkgs.nerd-icons-corfu # nerd icons for completion
epkgs.corfu # completion
epkgs.cape # completion extensions
epkgs.orderless # search paradigm
epkgs.yasnippet # snippets support
epkgs.yasnippet-snippets # commonly used snippets
epkgs.rg # ripgrep
epkgs.exec-path-from-shell # load env and path
epkgs.eat # better shell
epkgs.rust-mode # rust mode (when rust-ts doesn't cut it)
epkgs.rustic # more rust things
epkgs.nix-mode # nix lang
epkgs.hcl-mode # hashicorp file mode
epkgs.shell-pop # quick shell popup
epkgs.envrc # support for loading .envrc
epkgs.nixpkgs-fmt # format nix files
epkgs.f # string + file utilities
epkgs.gptel # llm chat (mainly claude)
epkgs.catppuccin-theme # catppuccin theme
epkgs.eldoc-box # docs in a box
epkgs.sideline # mainly for flymake errors on the side
epkgs.sideline-flymake # mainly for flymake errors on the side
epkgs.sideline-eglot # mainly for flymake errors on the side
];
};
home.sessionVariables = {
EDITOR = "emacs";
XDG_SCREENSHOTS_DIR = "~/screenshots";
};
home.file = {
emacs-init = {
source = ./early-init.el;
target = ".emacs.d/early-init.el";
};
emacs = {
source = ./init.el;
target = ".emacs.d/init.el";
};
};
services.nextcloud-client = {
enable = true;
};
}
+92
View File
@@ -0,0 +1,92 @@
;;; package --- early init -*- lexical-binding: t -*-
;;; Commentary:
;;; Prevents white flash and better Emacs defaults
;;; Code:
(set-language-environment "UTF-8")
(setq-default
default-frame-alist
'((background-color . "#1e1e2e")
(bottom-divider-width . 1) ; Thin horizontal window divider
(foreground-color . "#bac2de") ; Default foreground color
(fullscreen . maximized) ; Maximize the window by default
(horizontal-scroll-bars . nil) ; No horizontal scroll-bars
(left-fringe . 8) ; Thin left fringe
(menu-bar-lines . 0) ; No menu bar
(right-divider-width . 1) ; Thin vertical window divider
(right-fringe . 8) ; Thin right fringe
(tool-bar-lines . 0) ; No tool bar
(undecorated . t) ; Remove extraneous X decorations
(vertical-scroll-bars . nil)) ; No vertical scroll-bars
user-full-name "Henrov henrov" ; ME!
;; memory configuration
;; Higher garbage collection threshold, prevents frequent gc locks, reset later
gc-cons-threshold most-positive-fixnum
;; Ignore warnings for (obsolete) elisp compilations
byte-compile-warnings '(not obsolete)
;; And other log types completely
warning-suppress-log-types '((comp) (bytecomp))
;; Large files are okay in the new millenium.
large-file-warning-threshold 100000000
;; dont show garbage collection messages at startup, will reset later
garbage-collection-messages nil
;; native compilation
package-native-compile t
native-comp-warning-on-missing-source nil
native-comp-async-report-warnings-errors 'silent
;; Read more based on system pipe capacity
read-process-output-max (max (* 10240 10240) read-process-output-max)
;; scroll configuration
scroll-margin 0 ; Lets scroll to the end of the margin
scroll-conservatively 100000 ; Never recenter the window
scroll-preserve-screen-position 1 ; Scrolling back and forth
;; frame config
;; Improve emacs startup time by not resizing to adjust for custom settings
frame-inhibit-implied-resize t
;; Dont resize based on character height / width but to exact pixels
frame-resize-pixelwise t
;; backups & files
backup-directory-alist '(("." . "~/.backups/")) ; Don't clutter
backup-by-copying t ; Don't clobber symlinks
create-lockfiles nil ; Don't have temp files
delete-old-versions t ; Cleanup automatically
kept-new-versions 6 ; Update every few times
kept-old-versions 2 ; And cleanup even more
version-control t ; Version them backups
delete-by-moving-to-trash t ; Dont delete, send to trash instead
;; startup
inhibit-startup-screen t ; I have already done the tutorial. Twice
inhibit-startup-message t ; I know I am ready
inhibit-startup-echo-area-message t ; Yep, still know it
initial-scratch-message nil ; I know it is the scratch buffer!
initial-buffer-choice nil
inhibit-startup-buffer-menu t
inhibit-x-resources t
initial-major-mode 'fundamental-mode
pgtk-wait-for-event-timeout 0.001 ; faster child frames
ad-redefinition-action 'accept ; dont care about legacy things being redefined
inhibit-compacting-font-caches t
;; tabs
tab-width 4 ; Always tab 4 spaces.
indent-tabs-mode nil ; Never use actual tabs.
;; rendering
cursor-in-non-selected-windows nil ; dont render cursors other windows
;; packages
use-package-always-defer t
load-prefer-newer t
default-input-method nil
use-dialog-box nil
use-file-dialog nil
use-package-expand-minimally t
package-enable-at-startup nil
use-package-enable-imenu-support t
auto-mode-case-fold nil ; No second pass of case-insensitive search over auto-mode-alist.
package-archives '(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa-stable" . "https://stable.melpa.org/packages/"))
package-archive-priorities '(("gnu" . 99)
("nongnu" . 80)
("melpa" . 70)
("melpa-stable" . 50))
)
;;; early-init.el ends here
+400
View File
@@ -0,0 +1,400 @@
;;; package --- Summary - My minimal Emacs init file -*- lexical-binding: t -*-
;;; Commentary:
;;; Simple Emacs setup I carry everywhere
;;; Code:
(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file 'noerror) ;; no error on missing custom file
(require 'package)
(package-initialize)
(defun reset-custom-vars ()
"Resets the custom variables that were set to crazy numbers"
(setopt gc-cons-threshold (* 1024 1024 100))
(setopt garbage-collection-messages t))
(use-package emacs
:custom
(native-comp-async-query-on-exit t)
(read-answer-short t)
(use-short-answers t)
(enable-recursive-minibuffers t)
(which-func-update-delay 1.0)
(visible-bell nil)
(custom-buffer-done-kill t)
(whitespace-line-column nil)
(x-underline-at-descent-line t)
(imenu-auto-rescan t)
(uniquify-buffer-name-style 'forward)
(confirm-nonexistent-file-or-buffer nil)
(create-lockfiles nil)
(make-backup-files nil)
(kill-do-not-save-duplicates t)
(sentence-end-double-space nil)
(treesit-enabled-modes t)
:init
;; base visual
(menu-bar-mode -1) ;; no menu bar
(toggle-scroll-bar -1) ;; no scroll bar
(tool-bar-mode -1) ;; no tool bar either
(blink-cursor-mode -1) ;; stop blinking
;; font of the century
(set-frame-font "Aporetic Sans Mono 12" nil t)
:bind
(("C-<wheel-up>" . pixel-scroll-precision) ; dont zoom in please, just scroll
("C-<wheel-down>" . pixel-scroll-precision) ; dont zoom in either, just scroll
("C-x k" . kill-current-buffer)) ; kill the buffer, dont ask
:hook
(text-mode . delete-trailing-whitespace-mode)
(prog-mode . delete-trailing-whitespace-mode)
(after-init . global-display-line-numbers-mode) ;; always show line numbers
(after-init . column-number-mode) ;; column number in the mode line
(after-init . size-indication-mode) ;; file size in the mode line
(after-init . pixel-scroll-precision-mode) ;; smooth mouse scroll
(after-init . electric-pair-mode) ;; i mean ... parens should auto create
(after-init . reset-custom-vars)
)
(use-package autorevert
:ensure nil
:custom
(auto-revert-interval 3)
(auto-revert-remote-files nil)
(auto-revert-use-notify t)
(auto-revert-avoid-polling nil)
(auto-revert-verbose t)
:hook
(after-init . global-auto-revert-mode))
(use-package recentf
:ensure nil
:commands (recentf-mode recentf-cleanup)
:hook
(after-init . recentf-mode)
:custom
(recentf-auto-cleanup 'never)
(recentf-exclude
(list "\\.tar$" "\\.tbz2$" "\\.tbz$" "\\.tgz$" "\\.bz2$"
"\\.bz$" "\\.gz$" "\\.gzip$" "\\.xz$" "\\.zip$"
"\\.7z$" "\\.rar$"
"COMMIT_EDITMSG\\'"
"\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\|bmp\\|xpm\\)$"
"-autoloads\\.el$" "autoload\\.el$"))
:config
;; A cleanup depth of -90 ensures that `recentf-cleanup' runs before
;; `recentf-save-list', allowing stale entries to be removed before the list
;; is saved by `recentf-save-list', which is automatically added to
;; `kill-emacs-hook' by `recentf-mode'.
(add-hook 'kill-emacs-hook #'recentf-cleanup -90))
(use-package savehist
:ensure nil
:commands (savehist-mode savehist-save)
:hook
(after-init . savehist-mode)
:custom
(savehist-autosave-interval 600)
(savehist-additional-variables
'(kill-ring ; clipboard
register-alist ; macros
mark-ring global-mark-ring ; marks
search-ring regexp-search-ring)))
(use-package hl-line
:ensure nil
:custom
(hl-line-sticky-flag nil)
(global-hl-line-sticky-flag nil)
:hook
(after-init . global-hl-line-mode))
(use-package saveplace
:ensure nil
:commands (save-place-mode save-place-local-mode)
:hook
(after-init . save-place-mode)
:custom
(save-place-limit 400))
(use-package nerd-icons
:custom
;; disable bright icon colors
(nerd-icons-color-icons nil))hells.nix
(use-package doom-modeline
:custom
(inhibit-compacting-font-caches t) ;; speed
(doom-modeline-buffer-file-name-style 'relative-from-project)
(doom-modeline-major-mode-icon nil) ;; distracting icons, no thank you
(doom-modeline-buffer-encoding nil) ;; everything is utf-8 anyway
(doom-modeline-buffer-state-icon nil) ;; the filename already shows me
(doom-modeline-lsp nil) ;; lsp state is too distracting, too often
:hook (after-init . doom-modeline-mode))
(load-theme 'catppuccin :no-confirm)
(use-package diminish :demand t) ;; declutter the modeline
(use-package eldoc
:diminish eldoc-mode
:custom
(eldoc-echo-area-use-multiline-p nil)) ;; docs for everything
(use-package eldoc-box
:defer t
:config
(set-face-background 'eldoc-box-border (catppuccin-color 'green))
(set-face-background 'eldoc-box-body (catppuccin-color 'base))
:bind
(("M-h" . eldoc-box-help-at-point)))
(use-package pulsar
:commands pulsar-global-mode pulsar-recenter-top pulsar-reveal-entry
:init
(defface pulsar-catppuccin
`((default :extend t)
(((class color) (min-colors 88) (background light))
:background ,(catppuccin-color 'sapphire))
(((class color) (min-colors 88) (background dark))
:background ,(catppuccin-color 'sapphire))
(t :inverse-video t))
"Alternative nord face for `pulsar-face'."
:group 'pulsar-faces)
:custom
(pulsar-face 'pulsar-catppuccin)
:hook
(after-init . pulsar-global-mode))
(use-package which-key
:commands which-key-mode
:diminish which-key-mode
:hook
(after-init . which-key-mode))
(use-package expreg
:bind ("M-m" . expreg-expand))
(use-package vundo) ;; undo tree
;; better structured editing
(use-package puni
:commands puni-global-mode
:hook
(after-init . puni-global-mode))
(use-package avy
:bind
("M-i" . avy-goto-char-2)
:custom
(avy-background t))
(use-package consult
:bind
("C-x b" . consult-buffer) ;; orig. switch-to-buffer
("M-y" . consult-yank-pop) ;; orig. yank-pop
("M-g M-g" . consult-goto-line) ;; orig. goto-line
("M-g i" . consult-imenu) ;; consult version is interactive
("M-g r" . consult-ripgrep) ;; find in project also works
:custom
(consult-narrow-key "<"))
(use-package vertico
:commands vertico-mode
:custom
(read-file-name-completion-ignore-case t)
(read-buffer-completion-ignore-case t)
(completion-ignore-case t)
(enable-recursive-minibuffers t)
(minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt))
:init
(vertico-mode)
:hook
(minibuffer-setup-hook . cursor-intangible-mode))
(use-package marginalia
:commands marginalia-mode
:hook (after-init . marginalia-mode))
(use-package crux
:bind
("C-c M-e" . crux-find-user-init-file)
("C-c C-w" . crux-transpose-windows)
("C-c M-d" . crux-find-current-directory-dir-locals-file)
("C-a" . crux-move-beginning-of-line))
(use-package magit
:bind (("C-M-g" . magit-status)))
(use-package nerd-icons-corfu
:commands nerd-icons-corfu-formatter
:defines corfu-margin-formatters)
(use-package corfu
:commands global-corfu-mode
:custom
(corfu-cycle t)
(corfu-auto t)
(corfu-auto-delay 1)
(corfu-auto-prefix 3)
(corfu-separator ?_)
:hook
(after-init . global-corfu-mode)
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package cape)
(use-package orderless
:custom
(completion-styles '(orderless partial-completion basic))
(completion-category-defaults nil)
(completion-category-overrides nil))
(use-package yasnippet
:commands yas-global-mode
:diminish yas-minor-mode
:hook
(after-init . yas-global-mode))
(use-package yasnippet-snippets :after yasnippet)
(use-package exec-path-from-shell
:commands exec-path-from-shell-initialize
:custom
(exec-path-from-shell-arguments nil)
:hook
(after-init . exec-path-from-shell-initialize))
(use-package nixpkgs-fmt
:custom
(nixpkgs-fmt-command "nixfmt"))
(use-package eat
:bind
(("C-c e p" . eat-project)
("C-c e t" . eat)))
(use-package f :demand t)
(use-package envrc
:commands envrc-global-mode
:hook
(after-init . envrc-global-mode))
(use-package gptel
:commands gptel-make-anthropic f-read-text
:config
(gptel-make-anthropic "Claude"
:stream t :key (f-read-text "/run/secrets/claude_key")))
(use-package sideline-flymake)
(use-package sideline-eglot)
(use-package sideline
:custom
(sideline-backends-right '(sideline-flymake sideline-eglot))
:hook
(eglot-managed-mode . sideline-mode)
(flymake-mode . sideline-mode))
(use-package eglot
:custom
(eglot-extend-to-xref t)
(eglot-ignored-server-capabilities '(:inlayHintProvider))
(jsonrpc-event-hook nil)
:hook
(eglot-managed-mode . eldoc-box-hover-mode)
(before-save . eldoc-format-buffer)
:bind
(:map eglot-mode-map
("C-c l a" . eglot-code-actions)
("C-c l r" . eglot-rename)
("C-c l h" . eldoc)
("C-c l g" . xref-find-references)
("C-c l w" . eglot-reconnect)))
(use-package proced
:custom
(proced-auto-update-flag t)
(proced-auto-update-interval 3)
(proced-enable-color-flag t)
(proced-show-remote-processes t))
(use-package org
:ensure t
:defer t
:commands (org-mode org-capture org-agenda)
:init
(defvar org-journal-file "~/nextcloud/org/journal.org")
(defvar org-archive-file "~/nextcloud/org/archive.org")
(defvar org-notes-file "~/nextcloud/org/notes.org")
(defvar org-inbox-file "~/nextcloud/org/inbox.org")
(defvar org-work-file "~/nextcloud/org/work.org")
(defun my/org-capture-project-target-heading ()
"Determine Org target headings from the current file's project path.
This function assumes a directory structure like '~/projects/COMPANY/PROJECT/'.
It extracts 'COMPANY' and 'PROJECT' to use as nested headlines
for an Org capture template.
If the current buffer is not visi
ting a file within such a
project structure, it returns nil, causing capture to default to
the top of the file."
(when-let* ((path (buffer-file-name))) ; Ensure we are in a file-visiting buffer
(let ((path-parts (split-string path "/" t " ")))
(when-let* ((projects-pos (cl-position "projects" path-parts :test #'string=))
(company (nth (+ 1 projects-pos) path-parts))
(project (nth (+ 2 projects-pos) path-parts)))
;; Return a list of headlines for Org to find or create.
(list company project)))))
:bind
(("C-c c" . org-capture)
("C-c i" . org-store-link)
("C-c a" . org-agenda)
:map org-mode-map
("C-c t" . org-toggle-inline-images)
("C-c l" . org-toggle-link-display))
:custom
(org-agenda-files (list org-inbox-file org-journal-file))
(org-directory "~/nextcloud/org")
(org-default-notes-file org-inbox-file)
(org-archive-location (concat org-archive-file "::* From %s"))
(org-log-done 'time)
(org-log-into-drawer t)
(org-hide-emphasis-markers t)
(org-src-fontify-natively t)
(org-src-tab-acts-natively t)
(org-capture-templates '(("t" "Todo" entry (file org-inbox-file)
"* TODO %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n\n%a\n\n)")
("j" "Journal" entry (file+olp+datetree org-journal-file)
"* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n\n%a\n\n")
("n" "Note" entry (file org-notes-file)
"* %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n\n%a\n\n")
("p" "Project Task" item
(file+function org-work-file my/org-capture-project-target-heading)
"* TODO %? \n CLOCK: %U"
))
)
:config
;; Enable syntax highlighting in code blocks
(add-hook 'org-mode-hook 'turn-on-font-lock)
(add-hook 'org-mode-hook 'org-indent-mode))
;; extras
(use-package comp-run
:ensure nil
:config
(push "tramp-loaddefs.el.gz" native-comp-jit-compilation-deny-list)
(push "cl-loaddefs.el.gz" native-comp-jit-compilation-deny-list))
(use-package rustic
:custom
(rustic-lsp-client 'eglot))
(provide 'init)
;;; init.el ends here
+29
View File
@@ -0,0 +1,29 @@
{ config, pkgs, lib, flakeRoot, ... }:
let
catppuccinMochaConf =
builtins.readFile (flakeRoot + "/assets/conf/dev/terminal/Catppuccin-Mocha.conf");
# Your own keymaps / other settings (but we will NOT rely on it for opacity)
repoKittyConfText =
builtins.readFile (flakeRoot + "/assets/conf/dev/terminal/kitty.conf");
in
{
xdg.enable = true;
# Stable theme file so kitty.conf can include it without /nix/store paths
xdg.configFile."kitty/themes/Catppuccin-Mocha.conf".text = catppuccinMochaConf;
programs.kitty = {
enable = true;
# Home Manager generates ~/.config/kitty/kitty.conf; we append in-order:
# 1) include theme
# 2) your repo config (keymaps etc.)
# 3) force opacity LAST so it always wins
extraConfig = ''
# 1) Theme first (stable path)
include themes/Catppuccin-Mocha.conf
# 2) Your repo config (may also include theme; harmless if duplicated)
${repoKittyConfText}
# 3) Force transparency last (wins)
#background_opacity 0.60
#dynamic_background_opacity yes
'';
};
}
+206
View File
@@ -0,0 +1,206 @@
# shells.nix — Home-Manager module
#
# Reads:
# ${flakeRoot}/assets/conf/shells.nixdev/terminal/enabled_shells.conf
# ${flakeRoot}/assets/conf/dev/terminal/aliases.conf
#
# For each enabled shell in [enabled_shells]:
# - installs/enables shell (where HM has an enable option)
# - if ${flakeRoot}/assets/conf/dev/terminal/<shell>.conf exists, sources it
# - ensures a *user-editable* aliases file exists in the shells default location
# - if a shell is disabled, its aliases file is removed
# .
# Notes on “editable”:
# - We do NOT manage the aliases file with xdg.configFile/home.file (those would be overwritten).
# - Instead, we create/remove files via home.activation (create only if missing).
{ config, pkgs, lib, flakeRoot, ... }:
let
terminalDir = flakeRoot + "/assets/conf/dev/terminal";
enabledFile = terminalDir + "/enabled_shells.conf";
aliasesFile = terminalDir + "/aliases.conf";
trim = lib.strings.trim;
# ---------- minimal INI-ish parser (sections + raw lines) ----------
readMaybe = p: if builtins.pathExists p then builtins.readFile p else "";
normalizeLine = l: trim (lib.replaceStrings [ "\r" ] [ "" ] l);
parseSections = text:
let
lines = map normalizeLine (lib.splitString "\n" text);
isHeader = l:
let s = l;
in lib.hasPrefix "[" s
&& lib.hasSuffix "]" s
&& builtins.stringLength s >= 3;
nameOf = l: lib.removeSuffix "]" (lib.removePrefix "[" l);
folded =
builtins.foldl'
(st: l:
if l == "" then st else
if isHeader l then st // { current = nameOf l; }
else
let
cur = st.current;
prev = st.sections.${cur} or [];
in
st // { sections = st.sections // { ${cur} = prev ++ [ l ]; }; }
)
{ current = "__root__"; sections = {}; }
lines;
in
folded.sections;
enabledSections = parseSections (readMaybe enabledFile);
aliasSections = parseSections (readMaybe aliasesFile);
# [enabled_shells] lines: key = yes/no
enabledShells =
let
raw = enabledSections.enabled_shells or [];
parseKV = l:
let m = builtins.match ''^([A-Za-z0-9_-]+)[[:space:]]*=[[:space:]]*(.*)$'' l;
in if m == null then null else {
k = trim (builtins.elemAt m 0);
v = lib.toLower (trim (builtins.elemAt m 1));
};
kvs = builtins.filter (x: x != null) (map parseKV raw);
in
map (x: x.k) (builtins.filter (x: x.v == "yes" || x.v == "true" || x.v == "1") kvs);
shellEnabled = shell: builtins.elem shell enabledShells;
# ---------- per-shell repo config file (<shell>.conf) ----------
shellConfPath = shell: terminalDir + "/${shell}.conf";
shellConfExists = shell: builtins.pathExists (shellConfPath shell);
sourceIfExistsSh = p: ''
if [ -f "${toString p}" ]; then
source "${toString p}"
fi
'';
# ---------- aliases section helpers ----------
secLines = name: aliasSections.${name} or [];
secText = name: lib.concatStringsSep "\n" (secLines name);
# Default alias-file locations
bashAliasesPath = "${config.home.homeDirectory}/.bash_aliases";
zshAliasesPath = "${config.home.homeDirectory}/.zsh_aliases";
fishAliasesPath = "${config.xdg.configHome}/fish/conf.d/aliases.fish";
# Seeds (created once; user can edit afterwards)
bashSeed = ''
# Created once from: ${toString aliasesFile}
# Edit freely; Home Manager will not overwrite this file.
#
${secText "bash_zsh"}
${secText "bash_specific"}
'';
zshSeed = ''
# Created once from: ${toString aliasesFile}
# Edit freely; Home Manager will not overwrite this file.
${secText "bash_zsh"}
${secText "zsh_specific"}
'';
# Fish: translate [bash_zsh] POSIX alias lines + append [fish_specific] as-is
parsePosixAlias = l:
let
m = builtins.match ''^[[:space:]]*alias[[:space:]]+([A-Za-z0-9_+-]+)=(.*)$'' l;
in
if m == null then null else
let
name = trim (builtins.elemAt m 0);
rhs0 = trim (builtins.elemAt m 1);
unquote =
if lib.hasPrefix "'" rhs0 && lib.hasSuffix "'" rhs0 then
lib.removeSuffix "'" (lib.removePrefix "'" rhs0)
else if lib.hasPrefix "\"" rhs0 && lib.hasSuffix "\"" rhs0 then
lib.removeSuffix "\"" (lib.removePrefix "\"" rhs0)
else
rhs0;
in
{ inherit name; cmd = unquote; };
escapeForFish = s:
lib.replaceStrings
[ "\\" "\"" "$" "`" ]
[ "\\\\" "\\\"" "\\$" "\\`" ]
s;
fishTranslated =
let
parsed = builtins.filter (x: x != null) (map parsePosixAlias (secLines "bash_zsh"));
in
lib.concatStringsSep "\n" (map (a: ''alias ${a.name} "${escapeForFish a.cmd}"'') parsed);
fishSeed = ''
# Created once from: ${toString aliasesFile}
# Edit freely; Home Manager will not overwrite this file.
status is-interactive; or exit
# Translated from [bash_zsh]:
${fishTranslated}
# From [fish_specific]:
${secText "fish_specific"}
'';
in
{
xdg.enable = true;
# Install/enable shells (no login-shell changes)
programs.bash.enable = shellEnabled "bash";
programs.zsh.enable = shellEnabled "zsh";
programs.fish.enable = shellEnabled "fish";
home.packages =
(lib.optionals (shellEnabled "dash") [ pkgs.dash ]) ++
(lib.optionals (shellEnabled "nushell") [ pkgs.nushell ]);
# Source per-shell repo config (if present) AND source the user alias file (if it exists).
# Important: define each option only ONCE.
programs.bash.bashrcExtra = lib.mkIf (shellEnabled "bash") (lib.mkAfter ''
${lib.optionalString (shellConfExists "bash") (sourceIfExistsSh (shellConfPath "bash"))}
if [ -f "${bashAliasesPath}" ]; then
source "${bashAliasesPath}"
fi
'');
programs.zsh.initContent = lib.mkIf (shellEnabled "zsh") (lib.mkAfter ''
${lib.optionalString (shellConfExists "zsh") (sourceIfExistsSh (shellConfPath "zsh"))}
if [ -f "${zshAliasesPath}" ]; then
source "${zshAliasesPath}"
fi
'');
programs.fish.interactiveShellInit = lib.mkIf (shellEnabled "fish") (lib.mkAfter ''
${lib.optionalString (shellConfExists "fish") ''
if test -f "${toString (shellConfPath "fish")}"
source "${toString (shellConfPath "fish")}"
end
''}
if test -f "${fishAliasesPath}"
source "${fishAliasesPath}"
end
'');
# Create/remove alias files based on enabled shells
home.activation.shellAliasesFiles = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
set -euo pipefail
# bash -------------------------------------------------------
if ${if shellEnabled "bash" then "true" else "false"}; then
cat > "${bashAliasesPath}" <<'EOF'
${bashSeed}
EOF
else
rm -f "${bashAliasesPath}"
fi
# zsh -------------------------------------------------------
if ${if shellEnabled "zsh" then "true" else "false"}; then
cat > "${zshAliasesPath}" <<'EOF'
${zshSeed}
EOF
else
rm -f "${zshAliasesPath}"
fi
# fish -------------------------------------------------------
if ${if shellEnabled "fish" then "true" else "false"}; then
mkdir -p "$(dirname "${fishAliasesPath}")"
cat > "${fishAliasesPath}" <<'EOF'
${fishSeed}
EOF
else
rm -f "${fishAliasesPath}"
fi
# fish
if ${if shellEnabled "fish" then "true" else "false"}; then
mkdir -p "$(dirname "${fishAliasesPath}")"
if [ ! -f "${fishAliasesPath}" ]; then
cat > "${fishAliasesPath}" <<'EOF'
${fishSeed}
EOF
fi
else
rm -f "${fishAliasesPath}"
fi
'';
}
+21
View File
@@ -0,0 +1,21 @@
{ config, pkgs, lib, flakeRoot, ... }:
let
repoStarshipToml = flakeRoot + "/assets/conf/dev/terminal/starship.toml";
# The exact key that appears in the error:
targetKey = "${config.home.homeDirectory}/.config/starship.toml";
in
{
xdg.enable = true;
programs.starship = {
enable = true;
enableZshIntegration = true;
enableBashIntegration = true;
enableFishIntegration = true;
};
# Force the *actual conflicting option* (home.file."<abs path>".source)
home.file."${targetKey}".source = lib.mkForce repoStarshipToml;
}
+26
View File
@@ -0,0 +1,26 @@
{ config, pkgs, lib, flakeRoot, ... }:
{
programs.zsh = {
enable = true;
enableCompletion = true;
autocd = true;
# Optional but recommended: keep zsh config in one dir (relative to $HOME)
dotDir = ".config/zsh";
oh-my-zsh = {
enable = true;
theme = "";
plugins = [
"git"
"sudo"
"extract"
"colored-man-pages"
"command-not-found"
"history"
"docker"
"kubectl"
];
};
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
};
}
+9 -9
View File
@@ -10,16 +10,16 @@ emacs README.org --batch -f org-babel-tangle
git add . git add .
git commit -m "experiment: local change" git commit -m "experiment: local change"
sudo nixos-rebuild test --flake .#your_hostname sudo nixos-rebuild test --flake .#YOUR_HOSTNAME
emacs README.org --batch -f org-babel-tangle && emacs --batch -l org -l ox-html README.org -f org-html-export-to-html --kill emacs README.org --batch -f org-babel-tangle && emacs --batch -l org -l ox-html README.org -f org-html-export-to-html --kill
git add . git add .
git commit -m "literate: structural update" git commit -m "literate: structural update"
sudo nixos-rebuild test --flake .#your_hostname sudo nixos-rebuild test --flake .#YOUR_HOSTNAME
- a NIXOS system installed with a user with sudo rights. - a NIXOS system installed with a user with sudo rights.
- an internet connection - an internet connection
- the folder henrovnix as you find it here - the folder henrovnix_ok as you find it here
# Check if NetworkManager service is running # Check if NetworkManager service is running
systemctl status NetworkManager systemctl status NetworkManager
@@ -36,18 +36,18 @@ nmcli connection show --active
# Optional: show device status # Optional: show device status
nmcli device status nmcli device status
find ~/Repos/nixos/henrovnix \ find ~/Repos/nixos/henrovnix_ok \
-type d -name ".*" -prune -o \ -type d -name ".*" -prune -o \
-type f -print0 \ -type f -print0 \
| xargs -0 sed -i 's/=<defaultUser>=/your_user/g' | xargs -0 sed -i 's/=<defaultUser>=/YOUR_USER/g'
find ~/Repos/nixos/henrovnix \ find ~/Repos/nixos/henrovnix_ok \
-type d -name ".*" -prune -o \ -type d -name ".*" -prune -o \
-type f -print0 \ -type f -print0 \
| xargs -0 sed -i 's/machine1/your_hostname/g' | xargs -0 sed -i 's/machine1/YOUR_HOSTNAME/g'
mv ./machines/machine1 ./machines/your_hostname mv ./machines/machine1 ./machines/YOUR_HOSTNAME
nixos-generate-config nixos-generate-config
sudo nixos-rebuild switch --flake .#your_hostname sudo nixos-rebuild switch --flake .#YOUR_HOSTNAME