From 17328f708958a8e4e4a152e6473e8d505fd6b59a Mon Sep 17 00:00:00 2001 From: Jet Date: Thu, 23 Apr 2026 20:18:40 -0700 Subject: [PATCH] feat: new setup for work laptop --- configuration.nix | 16 - flake.nix | 50 +- home-modules/browser.nix | 71 ++ home-modules/core.nix | 25 + home-modules/default.nix | 15 + home-modules/desktop.nix | 125 +++ home-modules/git.nix | 62 ++ home-modules/lib.nix | 326 +++++++ home-modules/opencode.nix | 51 + home-modules/packages.nix | 67 ++ home-modules/shell.nix | 116 +++ home-modules/terminal.nix | 86 ++ home.nix | 916 +----------------- hosts/framework-work/default.nix | 21 + .../framework-work/hardware-configuration.nix | 47 + hosts/framework/default.nix | 21 + .../framework/hardware-configuration.nix | 5 - 17 files changed, 1063 insertions(+), 957 deletions(-) create mode 100644 home-modules/browser.nix create mode 100644 home-modules/core.nix create mode 100644 home-modules/default.nix create mode 100644 home-modules/desktop.nix create mode 100644 home-modules/git.nix create mode 100644 home-modules/lib.nix create mode 100644 home-modules/opencode.nix create mode 100644 home-modules/packages.nix create mode 100644 home-modules/shell.nix create mode 100644 home-modules/terminal.nix create mode 100644 hosts/framework-work/default.nix create mode 100644 hosts/framework-work/hardware-configuration.nix create mode 100644 hosts/framework/default.nix rename hardware-configuration.nix => hosts/framework/hardware-configuration.nix (70%) diff --git a/configuration.nix b/configuration.nix index 76b0954..57ed0fc 100644 --- a/configuration.nix +++ b/configuration.nix @@ -1,8 +1,6 @@ { config, pkgs, ... }: { - imports = [ ./hardware-configuration.nix ]; - boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.configurationLimit = 3; boot.loader.efi.canTouchEfiVariables = true; @@ -10,8 +8,6 @@ boot.loader.systemd-boot.consoleMode = "max"; boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; - networking.hostName = "framework"; - # Ensure current wireless firmware is available. hardware.enableRedistributableFirmware = true; @@ -280,18 +276,6 @@ "net.ipv4.tcp_congestion_control" = "bbr"; }; - # Use RAM disk (tmpfs) for temporary files - much faster than disk - fileSystems."/tmp" = { - device = "tmpfs"; - fsType = "tmpfs"; - options = [ - "size=32G" # Use up to 32GB RAM for /tmp (adjust as needed) - "mode=1777" - "nosuid" - "nodev" - ]; - }; - environment.systemPackages = with pkgs; [ bubblewrap docker diff --git a/flake.nix b/flake.nix index 98bceb1..2d4b3d0 100644 --- a/flake.nix +++ b/flake.nix @@ -31,31 +31,35 @@ nixos-hardware, ... }: + let + mkHost = hostname: nixpkgs.lib.nixosSystem { + modules = [ + { nixpkgs.hostPlatform = "x86_64-linux"; } + ./hosts/${hostname} + nixos-hardware.nixosModules.framework-amd-ai-300-series + home-manager.nixosModules.home-manager + inputs.nix-index-database.nixosModules.default + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.backupFileExtension = "backup"; + home-manager.extraSpecialArgs = { inherit inputs; }; + home-manager.users.jet = import ./home.nix; + } + { + nixpkgs.overlays = [ + inputs.nur.overlays.default + inputs.t3code.overlays.default + ]; + } + ]; + }; + in { formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt; nixosConfigurations = { - framework = nixpkgs.lib.nixosSystem { - modules = [ - { nixpkgs.hostPlatform = "x86_64-linux"; } - ./configuration.nix - nixos-hardware.nixosModules.framework-amd-ai-300-series - home-manager.nixosModules.home-manager - inputs.nix-index-database.nixosModules.default - { - home-manager.useGlobalPkgs = true; - home-manager.useUserPackages = true; - home-manager.backupFileExtension = "backup"; - home-manager.extraSpecialArgs = { inherit inputs; }; - home-manager.users.jet = import ./home.nix; - } - { - nixpkgs.overlays = [ - inputs.nur.overlays.default - inputs.t3code.overlays.default - ]; - } - ]; - }; + framework = mkHost "framework"; + framework-work = mkHost "framework-work"; }; devShells.x86_64-linux.default = @@ -63,7 +67,7 @@ pkgs = nixpkgs.legacyPackages.x86_64-linux; nhs = pkgs.writeShellScriptBin "nhs" '' sudo -v || exit $? - nh os switch --hostname framework path:. "$@" + nh os switch --hostname "$(${pkgs.hostname}/bin/hostname)" path:. "$@" ''; in pkgs.mkShell { diff --git a/home-modules/browser.nix b/home-modules/browser.nix new file mode 100644 index 0000000..c04df2a --- /dev/null +++ b/home-modules/browser.nix @@ -0,0 +1,71 @@ +{ pkgs, ... }: + +{ + programs.zen-browser = { + enable = true; + policies = { + DisableTelemetry = true; + DisableFirefoxStudies = true; + DisablePocket = true; + DontCheckDefaultBrowser = true; + DisableAppUpdate = true; + DisableMasterPasswordCreation = true; + DisablePasswordReveal = true; + DisableProfileImport = true; + ExtensionUpdate = false; + OfferToSaveLogins = false; + DisableFirefoxAccounts = true; + DisableFormHistory = true; + DisableSafeMode = true; + DisableSetDesktopBackground = true; + EnableTrackingProtection = { + Value = true; + Locked = true; + Cryptomining = true; + Fingerprinting = true; + }; + HardwareAcceleration = true; + NoDefaultBookmarks = true; + PasswordManagerEnabled = false; + Preferences = { + "zen.theme.border-radius" = 0; + "zen.theme.content-element-separation" = 0; + }; + }; + profiles.default = { + isDefault = true; + settings = { + "identity.fxaccounts.enabled" = false; + "ui.prefersReducedMotion" = 1; + }; + extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ + ublock-origin + onepassword-password-manager + sponsorblock + darkreader + vimium + return-youtube-dislikes + react-devtools + firefox-color + pay-by-privacy + translate-web-pages + user-agent-string-switcher + copy-selected-tabs-to-clipboard + dearrow + violentmonkey + tst-indent-line + ]; + search = { + default = "SearXNG"; + privateDefault = "SearXNG"; + force = true; + engines = { + "SearXNG" = { + urls = [ { template = "https://search.extremist.software/search?q={searchTerms}"; } ]; + definedAliases = [ "@s" ]; + }; + }; + }; + }; + }; +} diff --git a/home-modules/core.nix b/home-modules/core.nix new file mode 100644 index 0000000..3fcd571 --- /dev/null +++ b/home-modules/core.nix @@ -0,0 +1,25 @@ +{ config, inputs, ... }: + +{ + imports = [ inputs.zen-browser.homeModules.default ]; + + home.username = "jet"; + home.homeDirectory = "/home/jet"; + home.stateVersion = "25.05"; + + home.sessionVariables = { + BROWSER = "zen"; + TERMINAL = "kitty"; + }; + + xdg.userDirs = { + enable = true; + setSessionVariables = true; + }; + + gtk = { + enable = true; + gtk4.theme = config.gtk.theme; + gtk3.extraConfig.gtk-application-prefer-dark-theme = 1; + }; +} diff --git a/home-modules/default.nix b/home-modules/default.nix new file mode 100644 index 0000000..1d3394b --- /dev/null +++ b/home-modules/default.nix @@ -0,0 +1,15 @@ +{ ... }: + +{ + imports = [ + ./lib.nix + ./core.nix + ./packages.nix + ./git.nix + ./shell.nix + ./terminal.nix + ./browser.nix + ./desktop.nix + ./opencode.nix + ]; +} diff --git a/home-modules/desktop.nix b/home-modules/desktop.nix new file mode 100644 index 0000000..daaa1dc --- /dev/null +++ b/home-modules/desktop.nix @@ -0,0 +1,125 @@ +{ pkgs, homeLib, ... }: + +{ + dconf.settings = { + "org/gnome/desktop/interface" = { + clock-format = "12h"; + clock-show-weekday = true; + color-scheme = "prefer-dark"; + enable-animations = false; + enable-hot-corners = false; + }; + "org/gnome/system/location" = { + enabled = true; + }; + "org/gnome/settings-daemon/plugins/power" = { + sleep-inactive-ac-type = "nothing"; + }; + "org/gtk/gtk4/settings/file-chooser" = { + show-hidden = true; + }; + "org/gtk/settings/file-chooser" = { + clock-format = "12h"; + show-hidden = true; + }; + "org/gnome/desktop/peripherals/touchpad" = { + disable-while-typing = false; + }; + "org/gnome/shell" = { + disable-user-extensions = false; + enabled-extensions = [ + "hidetopbar@mathieu.bidon.ca" + "wifiqrcode@glerro.pm.me" + "system-monitor@paradoxxx.zero.gmail.com" + "clipboard-indicator@tudmotu.com" + "emoji-copy@felipeftn" + "tailscale-gnome-qs@tailscale-qs.github.io" + ]; + }; + }; + + xdg.desktopEntries.extract-here = { + name = "Extract Here"; + exec = "file-roller --extract-here %U"; + icon = "file-roller"; + type = "Application"; + categories = [ "Utility" ]; + mimeType = [ + "application/zip" + "application/x-tar" + "application/x-compressed-tar" + "application/x-bzip-compressed-tar" + "application/x-xz-compressed-tar" + "application/x-zstd-compressed-tar" + "application/gzip" + "application/x-7z-compressed" + "application/x-rar" + "application/x-rar-compressed" + ]; + noDisplay = true; + }; + + xdg.autostart = { + enable = true; + entries = [ + "${homeLib.zenStartup}/share/applications/zen-startup.desktop" + "${homeLib.kittyZellijStartup}/share/applications/kitty-zellij-startup.desktop" + "${homeLib.signalStartup}/share/applications/signal-startup.desktop" + "${pkgs.slack}/share/applications/slack.desktop" + "${homeLib.betterbirdStartup}/share/applications/betterbird-startup.desktop" + "${homeLib.vesktopStartup}/share/applications/vesktop-startup.desktop" + "${homeLib.zulipStartup}/share/applications/zulip-startup.desktop" + ]; + }; + + home.file.".local/share/gnome-shell/extensions/tailscale-gnome-qs@tailscale-qs.github.io" = { + source = "${homeLib.tailscaleQsExtension}/share/gnome-shell/extensions/tailscale-gnome-qs@tailscale-qs.github.io"; + recursive = true; + }; + + systemd.user.services.nasa-apod-wallpaper = { + Unit = { + Description = "Fetch NASA APOD wallpaper"; + After = [ "graphical-session.target" ]; + PartOf = [ "graphical-session.target" ]; + }; + Service = { + Type = "oneshot"; + ExecStart = "${homeLib.nasaApodWallpaper}/bin/nasa-apod-wallpaper"; + }; + }; + + systemd.user.timers.nasa-apod-wallpaper = { + Unit.Description = "Refresh NASA APOD wallpaper regularly"; + Timer = { + OnStartupSec = "2m"; + OnCalendar = "hourly"; + Persistent = true; + Unit = "nasa-apod-wallpaper.service"; + }; + Install.WantedBy = [ "timers.target" ]; + }; + + xdg.mimeApps = { + enable = true; + defaultApplications = { + "text/html" = "zen-beta.desktop"; + "x-scheme-handler/http" = "zen-beta.desktop"; + "x-scheme-handler/https" = "zen-beta.desktop"; + "x-scheme-handler/about" = "zen-beta.desktop"; + "x-scheme-handler/unknown" = "zen-beta.desktop"; + "x-scheme-handler/mailto" = "eu.betterbird.Betterbird.desktop"; + "inode/directory" = "org.gnome.Nautilus.desktop"; + "application/zip" = "org.gnome.FileRoller.desktop"; + "application/x-tar" = "org.gnome.FileRoller.desktop"; + "application/x-compressed-tar" = "org.gnome.FileRoller.desktop"; + "application/x-bzip-compressed-tar" = "org.gnome.FileRoller.desktop"; + "application/x-xz-compressed-tar" = "org.gnome.FileRoller.desktop"; + "application/x-zstd-compressed-tar" = "org.gnome.FileRoller.desktop"; + "application/gzip" = "org.gnome.FileRoller.desktop"; + "application/x-7z-compressed" = "org.gnome.FileRoller.desktop"; + "application/x-rar" = "org.gnome.FileRoller.desktop"; + "application/x-rar-compressed" = "org.gnome.FileRoller.desktop"; + }; + }; +} diff --git a/home-modules/git.nix b/home-modules/git.nix new file mode 100644 index 0000000..44b98c9 --- /dev/null +++ b/home-modules/git.nix @@ -0,0 +1,62 @@ +{ homeLib, ... }: + +{ + programs.git = { + enable = true; + settings = { + user.name = homeLib.name; + user.email = homeLib.email; + core.sshCommand = "ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3"; + core.compression = 6; + pack.windowMemory = "256m"; + pack.packSizeLimit = "2g"; + pack.threads = 1; + gpg.ssh.allowedSignersFile = "~/.config/git/allowed_signers"; + }; + signing = { + key = homeLib.sshSigningKey; + signByDefault = true; + format = "ssh"; + }; + }; + + home.file.".gitconfig".text = '' + # Compatibility shim for tools that only read ~/.gitconfig. + [include] + path = ~/.config/git/config + ''; + + home.file.".config/git/allowed_signers".text = builtins.concatStringsSep "" ( + map (publicKey: "${homeLib.email} ${publicKey}\n") homeLib.sshPublicKeys + ); + + programs.jujutsu = { + enable = true; + settings = { + remotes.origin.auto-track-bookmarks = "glob:*"; + user = { + name = homeLib.name; + email = homeLib.email; + }; + signing = { + behavior = "own"; + backend = "ssh"; + key = homeLib.sshSigningKey; + }; + git = { + sign-on-push = true; + }; + ui = { + default-command = "log"; + editor = "hx"; + pager = "bat --style=plain"; + }; + diff.tool = [ + "difft" + "--color=always" + "$left" + "$right" + ]; + }; + }; +} diff --git a/home-modules/lib.nix b/home-modules/lib.nix new file mode 100644 index 0000000..c272cd6 --- /dev/null +++ b/home-modules/lib.nix @@ -0,0 +1,326 @@ +{ + config, + pkgs, + inputs, + ... +}: + +let + name = "Jet"; + email = "jet@extremist.software"; + sshSigningKey = "~/.ssh/id_ed25519"; + sshPublicKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu jet@extremist.software" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPyic30I+SaDw0Lz/EFpMNeHCwxpwPfkgfR6uz3g7io7 jet@corp.primitive.dev" + ]; + tailscaleQsExtension = pkgs.stdenvNoCC.mkDerivation { + pname = "tailscale-gnome-qs"; + version = "5"; + src = pkgs.fetchzip { + url = "https://github.com/tailscale-qs/tailscale-gnome-qs/archive/refs/tags/v5.tar.gz"; + sha256 = "0b9jy8pyxvpkxf3adlwq42kii14jn5g7xyxggjzg87pb5jg4zfg2"; + }; + dontBuild = true; + installPhase = '' + mkdir -p "$out/share/gnome-shell/extensions" + cp -r "$src/tailscale-gnome-qs@tailscale-qs.github.io" \ + "$out/share/gnome-shell/extensions/tailscale-gnome-qs@tailscale-qs.github.io" + ''; + }; + wrappedOpencode = pkgs.symlinkJoin { + name = "opencode-wrapped"; + paths = [ pkgs.opencode ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram "$out/bin/opencode" \ + --prefix LD_LIBRARY_PATH : "${pkgs.lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib ]}" + ''; + }; + greptileSkills = pkgs.fetchFromGitHub { + owner = "greptileai"; + repo = "skills"; + rev = "4ae5198fb82fe28d7b452796152f2b1745051c77"; + hash = "sha256-NvDd3BSVeS10kYupLxo27VlKeeHPHrxyTb8EdVqrtQw="; + }; + nasaApodWallpaper = pkgs.writeShellApplication { + name = "nasa-apod-wallpaper"; + runtimeInputs = [ + pkgs.coreutils + pkgs.curl + pkgs.glib + pkgs.jq + ]; + text = '' + set -euo pipefail + + state_dir="${config.home.homeDirectory}/.local/state/nasa-apod" + current_link="$state_dir/current" + mkdir -p "$state_dir" + curl_args=( + --fail + --silent + --show-error + --location + --retry 30 + --retry-all-errors + --retry-delay 2 + --connect-timeout 10 + --max-time 300 + ) + + set_wallpaper() { + local target="$1" + + gsettings set org.gnome.desktop.background picture-uri "file://$target" + gsettings set org.gnome.desktop.background picture-uri-dark "file://$target" + gsettings set org.gnome.desktop.background picture-options 'zoom' + } + + json="$(curl "''${curl_args[@]}" 'https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY' || true)" + if [ -z "$json" ]; then + exit 0 + fi + + media_type="$(printf '%s' "$json" | jq -r '.media_type // empty')" + + if [ "$media_type" != "image" ]; then + exit 0 + fi + + image_url="$(printf '%s' "$json" | jq -r '.hdurl // .url // empty')" + if [ -z "$image_url" ]; then + exit 0 + fi + + ext="''${image_url##*.}" + ext="''${ext%%\?*}" + if [ -z "$ext" ] || [ "$ext" = "$image_url" ]; then + ext="jpg" + fi + + date_stamp="$(printf '%s' "$json" | jq -r '.date // empty')" + if [ -z "$date_stamp" ]; then + date_stamp="$(date +%F)" + fi + + target="$state_dir/apod-$date_stamp.$ext" + tmp="$target.tmp" + + if curl "''${curl_args[@]}" "$image_url" -o "$tmp" && [ -s "$tmp" ]; then + mv "$tmp" "$target" + ln -sfn "$target" "$current_link" + set_wallpaper "$target" + else + rm -f "$tmp" + fi + ''; + }; + zellijNewTabZoxide = pkgs.writeShellApplication { + name = "zellij-new-tab-zoxide"; + runtimeInputs = [ + pkgs.coreutils + pkgs.fzf + pkgs.zellij + pkgs.zoxide + ]; + text = '' + set -euo pipefail + + dirs="$(${pkgs.zoxide}/bin/zoxide query -l | while IFS= read -r dir; do + if [ -d "$dir" ]; then + printf '%s\t%s\n' "$(${pkgs.coreutils}/bin/basename "$dir")" "$dir" + fi + done)" + + if [ -z "$dirs" ]; then + if [ -n "''${ZELLIJ:-}" ]; then + exec ${pkgs.bashInteractive}/bin/bash -i + fi + exit 1 + fi + + dir="$(printf '%s\n' "$dirs" | ${pkgs.fzf}/bin/fzf \ + --delimiter='\t' \ + --with-nth='2' \ + --nth='1' \ + --height='40%' \ + --layout='reverse' \ + --border \ + --prompt='dir> ' \ + --exit-0 | ${pkgs.coreutils}/bin/cut -f2-)" + + if [ -z "$dir" ]; then + if [ -n "''${ZELLIJ:-}" ]; then + ${pkgs.zellij}/bin/zellij action close-tab >/dev/null 2>&1 || true + exit 0 + fi + exit 1 + fi + + tab_name="$(${pkgs.coreutils}/bin/basename "$dir")" + if [ "$dir" = "/" ]; then + tab_name="/" + fi + + cd "$dir" + + escape_kdl() { + local value="$1" + value="''${value//\\/\\\\}" + value="''${value//\"/\\\"}" + printf '%s' "$value" + } + + if [ -n "''${ZELLIJ:-}" ]; then + ${pkgs.zellij}/bin/zellij action rename-tab "$tab_name" >/dev/null 2>&1 || true + fi + + if [ -n "''${ZELLIJ:-}" ]; then + exec ${pkgs.bashInteractive}/bin/bash -i + fi + + layout_file="${config.home.homeDirectory}/.local/state/zellij-launch-layout.kdl" + mkdir -p "$(dirname "$layout_file")" + printf '%s\n' \ + 'layout {' \ + " tab name=\"$(escape_kdl "$tab_name")\" cwd=\"$(escape_kdl "$dir")\" {" \ + ' pane focus=true' \ + ' pane size=1 borderless=true {' \ + ' plugin location="compact-bar"' \ + ' }' \ + ' }' \ + '}' > "$layout_file" + + exec ${pkgs.zellij}/bin/zellij -l "$layout_file" + ''; + }; + zellijPersistentSession = pkgs.writeShellApplication { + name = "zellij-persistent-session"; + runtimeInputs = [ pkgs.zellij ]; + text = '' + set -euo pipefail + + while true; do + if ${pkgs.zellij}/bin/zellij attach --create main --force-run-commands; then + if ! ${zellijNewTabZoxide}/bin/zellij-new-tab-zoxide; then + exec ${pkgs.bashInteractive}/bin/bash -i + fi + else + exit $? + fi + done + ''; + }; + zellijSyncTabName = pkgs.writeShellApplication { + name = "zellij-sync-tab-name"; + runtimeInputs = [ + pkgs.coreutils + pkgs.jq + pkgs.zellij + ]; + text = '' + set -euo pipefail + + if [ -z "''${ZELLIJ:-}" ]; then + exit 0 + fi + + current_tab_info="$(${pkgs.zellij}/bin/zellij action current-tab-info --json 2>/dev/null)" + current_tab_id="$(printf '%s\n' "$current_tab_info" | ${pkgs.jq}/bin/jq -r '.tab_id // empty')" + current_tab_name="$(printf '%s\n' "$current_tab_info" | ${pkgs.jq}/bin/jq -r '.name // empty')" + + if [ -z "$current_tab_id" ]; then + exit 0 + fi + + next_tab_name="$(${pkgs.zellij}/bin/zellij action list-panes --json 2>/dev/null | ${pkgs.jq}/bin/jq -r --argjson tab_id "$current_tab_id" ' + [ .[] + | select((.is_plugin | not) and .tab_id == $tab_id) + | .pane_cwd // empty + | if . == "/" then "/" else split("/") | map(select(length > 0)) | last end + ] + | reduce .[] as $name ([]; if index($name) == null then . + [$name] else . end) + | join("-") + ' 2>/dev/null)" + + if [ -z "$next_tab_name" ] || [ "$next_tab_name" = "$current_tab_name" ]; then + exit 0 + fi + + exec ${pkgs.zellij}/bin/zellij action rename-tab "$next_tab_name" + ''; + }; + zenStartup = pkgs.makeDesktopItem { + name = "zen-startup"; + desktopName = "Zen Startup"; + comment = "Launch Zen Browser"; + exec = "${config.programs.zen-browser.package}/bin/zen-beta"; + terminal = false; + categories = [ "Network" ]; + }; + kittyZellijStartup = pkgs.makeDesktopItem { + name = "kitty-zellij-startup"; + desktopName = "Kitty Zellij Startup"; + comment = "Open Kitty and attach to the main Zellij session"; + exec = "${pkgs.kitty}/bin/kitty --start-as=fullscreen ${zellijPersistentSession}/bin/zellij-persistent-session"; + terminal = false; + categories = [ + "TerminalEmulator" + ]; + }; + vesktopStartup = pkgs.makeDesktopItem { + name = "vesktop-startup"; + desktopName = "Vesktop Startup"; + comment = "Launch Vesktop in fullscreen"; + exec = "${pkgs.vesktop}/bin/vesktop --start-fullscreen"; + terminal = false; + categories = [ "Network" ]; + }; + signalStartup = pkgs.makeDesktopItem { + name = "signal-startup"; + desktopName = "Signal Startup"; + comment = "Launch Signal in fullscreen"; + exec = "${pkgs.signal-desktop}/bin/signal-desktop --start-fullscreen"; + terminal = false; + categories = [ "Network" ]; + }; + betterbirdStartup = pkgs.makeDesktopItem { + name = "betterbird-startup"; + desktopName = "Betterbird Startup"; + comment = "Launch Betterbird in fullscreen"; + exec = "${pkgs.flatpak}/bin/flatpak run eu.betterbird.Betterbird --fullscreen"; + terminal = false; + categories = [ "Network" ]; + }; + zulipStartup = pkgs.makeDesktopItem { + name = "zulip-startup"; + desktopName = "Zulip Startup"; + comment = "Launch Zulip in fullscreen"; + exec = "${pkgs.zulip}/bin/zulip --start-fullscreen"; + terminal = false; + categories = [ "Network" ]; + }; +in +{ + _module.args.homeLib = { + inherit + betterbirdStartup + email + greptileSkills + kittyZellijStartup + name + nasaApodWallpaper + signalStartup + sshPublicKeys + sshSigningKey + tailscaleQsExtension + wrappedOpencode + zenStartup + zellijNewTabZoxide + zellijPersistentSession + zellijSyncTabName + zulipStartup + vesktopStartup + ; + }; +} diff --git a/home-modules/opencode.nix b/home-modules/opencode.nix new file mode 100644 index 0000000..1d67939 --- /dev/null +++ b/home-modules/opencode.nix @@ -0,0 +1,51 @@ +{ homeLib, ... }: + +{ + home.file.".agents/skills/check-pr".source = "${homeLib.greptileSkills}/check-pr"; + home.file.".agents/skills/greploop".source = "${homeLib.greptileSkills}/greploop"; + + home.file.".config/opencode/opencode.json".text = builtins.toJSON { + "$schema" = "https://opencode.ai/config.json"; + autoupdate = false; + plugin = [ "opencode-with-claude" ]; + permission = { + "*" = "allow"; + external_directory = "allow"; + doom_loop = "allow"; + }; + mcp.linear = { + type = "remote"; + url = "https://mcp.linear.app/mcp"; + enabled = true; + }; + model = "openai/gpt-5.4-fast"; + small_model = "openai/gpt-5.4-mini-fast"; + provider.openai.models."gpt-5.4-fast".options = { + reasoningEffort = "xhigh"; + }; + share = "disabled"; + }; + + home.file.".config/opencode/AGENTS.md".text = '' + # NixOS Rules + + - This machine is NixOS. Prefer the Nix way for installing and running tools. + - Do not suggest `apt`, `dnf`, `pacman`, `brew`, `npm -g`, `pip install`, `cargo install`, `curl | sh`, or manual installers unless explicitly asked. + - If a repo has `flake.nix`, treat it as the source of truth for project tooling. + - If a needed tool belongs to the project, add it to `flake.nix` or the dev shell instead of installing it another way. + - If a repo has `flake.nix`, ensure `.envrc` contains `use flake` unless the repo intentionally uses a different setup. + - If there is no `flake.nix` and the tool is only needed temporarily, prefer `nix shell nixpkgs# -c `. + - For persistent tools, prefer declarative Nix configuration. + - Prefer `direnv` or `nix develop` before deciding a tool is missing. + - Never run `nixos-rebuild`, `nh os switch`, `nhs`, or other system switch commands unless explicitly asked. + ''; + + home.file.".config/opencode/tui.json".text = builtins.toJSON { + "$schema" = "https://opencode.ai/tui.json"; + keybinds = { + leader = "ctrl+x"; + command_list = "p"; + variant_cycle = "t"; + }; + }; +} diff --git a/home-modules/packages.nix b/home-modules/packages.nix new file mode 100644 index 0000000..06a0944 --- /dev/null +++ b/home-modules/packages.nix @@ -0,0 +1,67 @@ +{ pkgs, homeLib, ... }: + +{ + home.packages = with pkgs; [ + bat + bun + claude-code + codex + ffmpeg-full + homeLib.wrappedOpencode + skills + homeLib.zellijNewTabZoxide + homeLib.zellijSyncTabName + fd + btop + fastfetch + gh + hyfetch + jq + mkp224o + nixfmt + difftastic + ripgrep + tea + trash-cli + tree + unzip + + rust-analyzer + typescript-language-server + nil + + element-desktop + file-roller + font-manager + foliate + gimp3 + google-chrome + handbrake + inkscape + kdePackages.kdenlive + libreoffice + logseq + obs-studio + pavucontrol + prismlauncher + qpwgraph + qbittorrent-enhanced + signal-desktop + slack + t3code + tor-browser + vesktop + vlc + zulip + linphone + lmstudio + + nerd-fonts.commit-mono + + gnomeExtensions.clipboard-indicator + gnomeExtensions.emoji-copy + gnomeExtensions.hide-top-bar + gnomeExtensions.system-monitor-next + gnomeExtensions.wifi-qrcode + ]; +} diff --git a/home-modules/shell.nix b/home-modules/shell.nix new file mode 100644 index 0000000..934df33 --- /dev/null +++ b/home-modules/shell.nix @@ -0,0 +1,116 @@ +{ ... }: + +{ + programs.helix = { + enable = true; + defaultEditor = true; + settings = { + theme = "dark_high_contrast"; + editor = { + line-number = "relative"; + lsp.display-messages = true; + lsp.display-inlay-hints = true; + end-of-line-diagnostics = "hint"; + inline-diagnostics = { + cursor-line = "hint"; + other-lines = "hint"; + }; + }; + }; + }; + + programs.starship = { + enable = true; + enableBashIntegration = true; + settings = { + format = "$directory$git_status$nix_shell$cmd_duration$line_break$character"; + directory.truncation_length = 3; + git_status.style = "red"; + git_branch.disabled = true; + nix_shell.format = "[$symbol]($style) "; + cmd_duration.min_time = 500; + character.success_symbol = "[❯](bold green)"; + character.error_symbol = "[❯](bold red)"; + }; + }; + + programs.eza = { + enable = true; + icons = "always"; + enableBashIntegration = true; + git = true; + extraOptions = [ + "--group-directories-first" + "--all" + ]; + }; + + programs.fzf = { + enable = true; + enableBashIntegration = true; + }; + + programs.zoxide = { + enable = true; + enableBashIntegration = true; + }; + + programs.direnv = { + enable = true; + enableBashIntegration = true; + nix-direnv.enable = true; + }; + + programs.bash = { + enable = true; + enableCompletion = true; + historyControl = [ + "ignoredups" + "erasedups" + ]; + historySize = 50000; + historyFileSize = 100000; + shellOptions = [ + "histappend" + "checkwinsize" + "globstar" + ]; + shellAliases = { + "dr" = "direnv reload"; + "da" = "direnv allow"; + "nfu" = "nix flake update"; + "o" = "opencode"; + ".." = "z .."; + j = "jj"; + jgf = "jj git fetch"; + jgp = "jj git push"; + jgc = "jj git clone --colocate"; + jbs = "jj bookmark set"; + jd = "jj describe"; + js = "jj show"; + jss = "jj show -s"; + jab = "jj abandon"; + jsp = "jj split"; + je = "jj edit --ignore-immutable"; + jall = "jj log -r 'all()'"; + jn = "jj new"; + jdiff = "jj diff"; + jsq = "jj squash"; + h = "hx"; + t = "trash"; + vanity = "mkp224o-amd64-64-24k -d noisebridgevanitytor noisebridge{2..7}"; + }; + initExtra = '' + # Automatically list directory contents when changing directories + auto_l_on_cd() { + if [ "$__LAST_PWD" != "$PWD" ]; then + l + __LAST_PWD="$PWD" + fi + } + + export PROMPT_COMMAND="auto_l_on_cd; $PROMPT_COMMAND" + __LAST_PWD="$PWD" + ''; + }; +} diff --git a/home-modules/terminal.nix b/home-modules/terminal.nix new file mode 100644 index 0000000..42c2ed7 --- /dev/null +++ b/home-modules/terminal.nix @@ -0,0 +1,86 @@ +{ pkgs, homeLib, ... }: + +{ + programs.zellij = { + enable = true; + enableBashIntegration = false; + + layouts.tabs-and-mode = '' + layout { + pane + pane size=1 borderless=true { + plugin location="status-bar" + } + pane size=1 borderless=true { + plugin location="tab-bar" + } + } + ''; + + layouts.zoxide-picker = '' + layout { + pane command="${homeLib.zellijNewTabZoxide}/bin/zellij-new-tab-zoxide" close_on_exit=true + pane size=1 borderless=true { + plugin location="compact-bar" + } + } + ''; + + settings = { + default_shell = "bash"; + default_layout = "zoxide-picker"; + pane_frames = false; + simplified_ui = true; + mouse_mode = true; + copy_on_select = true; + show_startup_tips = false; + show_release_notes = false; + attach_to_session = true; + session_name = "main"; + on_force_close = "detach"; + session_serialization = true; + serialize_pane_viewport = true; + + ui = { + pane_frames = { + hide_session_name = true; + }; + }; + }; + + extraConfig = '' + keybinds { + tab { + bind "n" { NewTab { layout "zoxide-picker"; }; SwitchToMode "Normal"; } + bind "N" { NewTab; SwitchToMode "Normal"; } + } + } + ''; + }; + + programs.kitty = { + enable = true; + settings = { + hide_window_decorations = "yes"; + draw_minimal_borders = "yes"; + font_family = "CommitMono Nerd Font"; + font_size = "12"; + confirm_os_window_close = "0"; + enable_audio_bell = "no"; + }; + themeFile = "GitHub_Dark_High_Contrast"; + }; + + xdg.desktopEntries.kitty = { + name = "Kitty"; + genericName = "Terminal Emulator"; + exec = "${pkgs.kitty}/bin/kitty --start-as=fullscreen ${homeLib.zellijPersistentSession}/bin/zellij-persistent-session"; + icon = "kitty"; + type = "Application"; + categories = [ + "System" + "TerminalEmulator" + ]; + comment = "Fast, featureful, GPU based terminal emulator"; + }; +} diff --git a/home.nix b/home.nix index 0d3470e..3bb7071 100644 --- a/home.nix +++ b/home.nix @@ -1,915 +1,5 @@ +{ ... }: + { - config, - pkgs, - inputs, - ... -}: - -let - name = "Jet"; - email = "jet@extremist.software"; - sshSigningKey = "~/.ssh/id_ed25519.pub"; - sshPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE40ISu3ydCqfdpb26JYD5cIN0Fu0id/FDS+xjB5zpqu"; - zenStartup = pkgs.makeDesktopItem { - name = "zen-startup"; - desktopName = "Zen Startup"; - comment = "Launch Zen Browser"; - exec = "${config.programs.zen-browser.package}/bin/zen-beta"; - terminal = false; - categories = [ "Network" ]; - }; - kittyZellijStartup = pkgs.makeDesktopItem { - name = "kitty-zellij-startup"; - desktopName = "Kitty Zellij Startup"; - comment = "Open Kitty and attach to the main Zellij session"; - exec = "${pkgs.kitty}/bin/kitty --start-as=fullscreen ${zellijPersistentSession}/bin/zellij-persistent-session"; - terminal = false; - categories = [ - "TerminalEmulator" - ]; - }; - vesktopStartup = pkgs.makeDesktopItem { - name = "vesktop-startup"; - desktopName = "Vesktop Startup"; - comment = "Launch Vesktop in fullscreen"; - exec = "${pkgs.vesktop}/bin/vesktop --start-fullscreen"; - terminal = false; - categories = [ "Network" ]; - }; - signalStartup = pkgs.makeDesktopItem { - name = "signal-startup"; - desktopName = "Signal Startup"; - comment = "Launch Signal in fullscreen"; - exec = "${pkgs.signal-desktop}/bin/signal-desktop --start-fullscreen"; - terminal = false; - categories = [ "Network" ]; - }; - betterbirdStartup = pkgs.makeDesktopItem { - name = "betterbird-startup"; - desktopName = "Betterbird Startup"; - comment = "Launch Betterbird in fullscreen"; - exec = "${pkgs.flatpak}/bin/flatpak run eu.betterbird.Betterbird --fullscreen"; - terminal = false; - categories = [ "Network" ]; - }; - zulipStartup = pkgs.makeDesktopItem { - name = "zulip-startup"; - desktopName = "Zulip Startup"; - comment = "Launch Zulip in fullscreen"; - exec = "${pkgs.zulip}/bin/zulip --start-fullscreen"; - terminal = false; - categories = [ "Network" ]; - }; - tailscaleQsExtension = pkgs.stdenvNoCC.mkDerivation { - pname = "tailscale-gnome-qs"; - version = "5"; - src = pkgs.fetchzip { - url = "https://github.com/tailscale-qs/tailscale-gnome-qs/archive/refs/tags/v5.tar.gz"; - sha256 = "0b9jy8pyxvpkxf3adlwq42kii14jn5g7xyxggjzg87pb5jg4zfg2"; - }; - dontBuild = true; - installPhase = '' - mkdir -p "$out/share/gnome-shell/extensions" - cp -r "$src/tailscale-gnome-qs@tailscale-qs.github.io" \ - "$out/share/gnome-shell/extensions/tailscale-gnome-qs@tailscale-qs.github.io" - ''; - }; - # opencode's native watcher binding needs libstdc++.so.6 on NixOS. - wrappedOpencode = pkgs.symlinkJoin { - name = "opencode-wrapped"; - paths = [ pkgs.opencode ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - wrapProgram "$out/bin/opencode" \ - --prefix LD_LIBRARY_PATH : "${pkgs.lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib ]}" - ''; - }; - greptileSkills = pkgs.fetchFromGitHub { - owner = "greptileai"; - repo = "skills"; - rev = "4ae5198fb82fe28d7b452796152f2b1745051c77"; - hash = "sha256-NvDd3BSVeS10kYupLxo27VlKeeHPHrxyTb8EdVqrtQw="; - }; - nasaApodWallpaper = pkgs.writeShellApplication { - name = "nasa-apod-wallpaper"; - runtimeInputs = [ - pkgs.coreutils - pkgs.curl - pkgs.glib - pkgs.jq - ]; - text = '' - set -euo pipefail - - state_dir="${config.home.homeDirectory}/.local/state/nasa-apod" - current_link="$state_dir/current" - mkdir -p "$state_dir" - curl_args=( - --fail - --silent - --show-error - --location - --retry 30 - --retry-all-errors - --retry-delay 2 - --connect-timeout 10 - --max-time 300 - ) - - set_wallpaper() { - local target="$1" - - gsettings set org.gnome.desktop.background picture-uri "file://$target" - gsettings set org.gnome.desktop.background picture-uri-dark "file://$target" - gsettings set org.gnome.desktop.background picture-options 'zoom' - } - - json="$(curl "''${curl_args[@]}" 'https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY' || true)" - if [ -z "$json" ]; then - exit 0 - fi - - media_type="$(printf '%s' "$json" | jq -r '.media_type // empty')" - - if [ "$media_type" != "image" ]; then - exit 0 - fi - - image_url="$(printf '%s' "$json" | jq -r '.hdurl // .url // empty')" - if [ -z "$image_url" ]; then - exit 0 - fi - - ext="''${image_url##*.}" - ext="''${ext%%\?*}" - if [ -z "$ext" ] || [ "$ext" = "$image_url" ]; then - ext="jpg" - fi - - date_stamp="$(printf '%s' "$json" | jq -r '.date // empty')" - if [ -z "$date_stamp" ]; then - date_stamp="$(date +%F)" - fi - - target="$state_dir/apod-$date_stamp.$ext" - tmp="$target.tmp" - - if curl "''${curl_args[@]}" "$image_url" -o "$tmp" && [ -s "$tmp" ]; then - mv "$tmp" "$target" - ln -sfn "$target" "$current_link" - set_wallpaper "$target" - else - rm -f "$tmp" - fi - ''; - }; - zellijNewTabZoxide = pkgs.writeShellApplication { - name = "zellij-new-tab-zoxide"; - runtimeInputs = [ - pkgs.coreutils - pkgs.fzf - pkgs.zellij - pkgs.zoxide - ]; - text = '' - set -euo pipefail - - dirs="$(${pkgs.zoxide}/bin/zoxide query -l | while IFS= read -r dir; do - if [ -d "$dir" ]; then - printf '%s\t%s\n' "$(${pkgs.coreutils}/bin/basename "$dir")" "$dir" - fi - done)" - - if [ -z "$dirs" ]; then - if [ -n "''${ZELLIJ:-}" ]; then - exec ${pkgs.bashInteractive}/bin/bash -i - fi - exit 1 - fi - - dir="$(printf '%s\n' "$dirs" | ${pkgs.fzf}/bin/fzf \ - --delimiter='\t' \ - --with-nth='2' \ - --nth='1' \ - --height='40%' \ - --layout='reverse' \ - --border \ - --prompt='dir> ' \ - --exit-0 | ${pkgs.coreutils}/bin/cut -f2-)" - - if [ -z "$dir" ]; then - if [ -n "''${ZELLIJ:-}" ]; then - ${pkgs.zellij}/bin/zellij action close-tab >/dev/null 2>&1 || true - exit 0 - fi - exit 1 - fi - - tab_name="$(${pkgs.coreutils}/bin/basename "$dir")" - if [ "$dir" = "/" ]; then - tab_name="/" - fi - - cd "$dir" - - escape_kdl() { - local value="$1" - value="''${value//\\/\\\\}" - value="''${value//\"/\\\"}" - printf '%s' "$value" - } - - if [ -n "''${ZELLIJ:-}" ]; then - ${pkgs.zellij}/bin/zellij action rename-tab "$tab_name" >/dev/null 2>&1 || true - fi - - if [ -n "''${ZELLIJ:-}" ]; then - exec ${pkgs.bashInteractive}/bin/bash -i - fi - - layout_file="${config.home.homeDirectory}/.local/state/zellij-launch-layout.kdl" - mkdir -p "$(dirname "$layout_file")" - printf '%s\n' \ - 'layout {' \ - " tab name=\"$(escape_kdl "$tab_name")\" cwd=\"$(escape_kdl "$dir")\" {" \ - ' pane focus=true' \ - ' pane size=1 borderless=true {' \ - ' plugin location="compact-bar"' \ - ' }' \ - ' }' \ - '}' > "$layout_file" - - exec ${pkgs.zellij}/bin/zellij -l "$layout_file" - ''; - }; - zellijPersistentSession = pkgs.writeShellApplication { - name = "zellij-persistent-session"; - runtimeInputs = [ pkgs.zellij ]; - text = '' - set -euo pipefail - - while true; do - if ${pkgs.zellij}/bin/zellij attach --create main --force-run-commands; then - if ! ${zellijNewTabZoxide}/bin/zellij-new-tab-zoxide; then - exec ${pkgs.bashInteractive}/bin/bash -i - fi - else - exit $? - fi - done - ''; - }; - zellijSyncTabName = pkgs.writeShellApplication { - name = "zellij-sync-tab-name"; - runtimeInputs = [ - pkgs.coreutils - pkgs.jq - pkgs.zellij - ]; - text = '' - set -euo pipefail - - if [ -z "''${ZELLIJ:-}" ]; then - exit 0 - fi - - current_tab_info="$(${pkgs.zellij}/bin/zellij action current-tab-info --json 2>/dev/null)" - current_tab_id="$(printf '%s\n' "$current_tab_info" | ${pkgs.jq}/bin/jq -r '.tab_id // empty')" - current_tab_name="$(printf '%s\n' "$current_tab_info" | ${pkgs.jq}/bin/jq -r '.name // empty')" - - if [ -z "$current_tab_id" ]; then - exit 0 - fi - - next_tab_name="$(${pkgs.zellij}/bin/zellij action list-panes --json 2>/dev/null | ${pkgs.jq}/bin/jq -r --argjson tab_id "$current_tab_id" ' - [ .[] - | select((.is_plugin | not) and .tab_id == $tab_id) - | .pane_cwd // empty - | if . == "/" then "/" else split("/") | map(select(length > 0)) | last end - ] - | reduce .[] as $name ([]; if index($name) == null then . + [$name] else . end) - | join("-") - ' 2>/dev/null)" - - if [ -z "$next_tab_name" ] || [ "$next_tab_name" = "$current_tab_name" ]; then - exit 0 - fi - - exec ${pkgs.zellij}/bin/zellij action rename-tab "$next_tab_name" - ''; - }; -in -{ - imports = [ inputs.zen-browser.homeModules.default ]; - - home.username = "jet"; - home.homeDirectory = "/home/jet"; - home.stateVersion = "25.05"; - - # Configure GNOME settings - dconf.settings = { - "org/gnome/desktop/interface" = { - clock-format = "12h"; - clock-show-weekday = true; - color-scheme = "prefer-dark"; - enable-animations = false; - enable-hot-corners = false; - }; - "org/gnome/system/location" = { - enabled = true; - }; - "org/gnome/settings-daemon/plugins/power" = { - sleep-inactive-ac-type = "nothing"; - }; - "org/gtk/gtk4/settings/file-chooser" = { - show-hidden = true; - }; - "org/gtk/settings/file-chooser" = { - clock-format = "12h"; - show-hidden = true; - }; - "org/gnome/desktop/peripherals/touchpad" = { - disable-while-typing = false; - }; - "org/gnome/shell" = { - disable-user-extensions = false; - enabled-extensions = [ - "hidetopbar@mathieu.bidon.ca" - "wifiqrcode@glerro.pm.me" - "system-monitor@paradoxxx.zero.gmail.com" - "clipboard-indicator@tudmotu.com" - "emoji-copy@felipeftn" - "tailscale-gnome-qs@tailscale-qs.github.io" - ]; - }; - }; - - home.packages = with pkgs; [ - # Scripts - (writeShellScriptBin "tea-init" '' - name="''${1:-$(basename "$PWD")}" - login="''${2:-git.extremist.software}" - user=$(tea logins list -o simple | awk -v l="$login" '$2 == "https://"l {print $4}') - if [ -z "$user" ]; then - echo "error: no tea login found for $login" >&2 - exit 1 - fi - tea repo create --name "$name" --login "$login" - git remote add origin "ssh://forgejo@''${login}/''${user}/''${name}.git" - '') - (writeShellScriptBin "ow" '' - sudo -v - sudo tailscale serve --bg 4096 - exec opencode web --hostname 127.0.0.1 --port 4096 - '') - - # CLI - bat - bun - claude-code - codex - ffmpeg-full - wrappedOpencode - skills - zellijNewTabZoxide - zellijSyncTabName - fd - btop - fastfetch - gh - hyfetch - jq - mkp224o - nixfmt - difftastic - ripgrep - tea - trash-cli - tree - unzip - - # LSP Servers - rust-analyzer - typescript-language-server - nil - - # Desktop - element-desktop - file-roller - font-manager - foliate - gimp3 - google-chrome - handbrake - inkscape - kdePackages.kdenlive - libreoffice - logseq - obs-studio - pavucontrol - prismlauncher - qpwgraph - qbittorrent-enhanced - signal-desktop - slack - t3code - tor-browser - vesktop - vlc - zulip - linphone - lmstudio - - # Fonts - nerd-fonts.commit-mono - - # GNOME Extensions - gnomeExtensions.clipboard-indicator - gnomeExtensions.emoji-copy - gnomeExtensions.hide-top-bar - gnomeExtensions.system-monitor-next - gnomeExtensions.wifi-qrcode - ]; - - home.sessionVariables = { - BROWSER = "zen"; - TERMINAL = "kitty"; - }; - - programs.git = { - enable = true; - settings = { - user.name = name; - user.email = email; - core.sshCommand = "ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3"; - core.compression = 6; - pack.windowMemory = "256m"; - pack.packSizeLimit = "2g"; - pack.threads = 1; - gpg.ssh.allowedSignersFile = "~/.config/git/allowed_signers"; - }; - signing = { - key = sshSigningKey; - signByDefault = true; - format = "ssh"; - }; - }; - - home.file.".gitconfig".text = '' - # Compatibility shim for tools that only read ~/.gitconfig. - [include] - path = ~/.config/git/config - ''; - - home.file.".config/git/allowed_signers".text = "${email} ${sshPublicKey}\n"; - - programs.helix = { - enable = true; - defaultEditor = true; - settings = { - theme = "dark_high_contrast"; - editor = { - line-number = "relative"; - lsp.display-messages = true; - lsp.display-inlay-hints = true; - end-of-line-diagnostics = "hint"; - inline-diagnostics = { - cursor-line = "hint"; - other-lines = "hint"; - }; - }; - }; - }; - - programs.starship = { - enable = true; - enableBashIntegration = true; - settings = { - format = "$directory$git_status$nix_shell$cmd_duration$line_break$character"; - directory.truncation_length = 3; - git_status.style = "red"; - git_branch.disabled = true; - nix_shell.format = "[$symbol]($style) "; - cmd_duration.min_time = 500; - character.success_symbol = "[❯](bold green)"; - character.error_symbol = "[❯](bold red)"; - }; - }; - - programs.zellij = { - enable = true; - enableBashIntegration = false; - - layouts.tabs-and-mode = '' - layout { - pane - pane size=1 borderless=true { - plugin location="status-bar" - } - pane size=1 borderless=true { - plugin location="tab-bar" - } - } - ''; - - layouts.zoxide-picker = '' - layout { - pane command="${zellijNewTabZoxide}/bin/zellij-new-tab-zoxide" close_on_exit=true - pane size=1 borderless=true { - plugin location="compact-bar" - } - } - ''; - - settings = { - # Default shell (using bash as configured in your system) - default_shell = "bash"; - default_layout = "zoxide-picker"; - pane_frames = false; - simplified_ui = true; - - # Mouse and interaction settings - enable for proper pane handling - mouse_mode = true; - copy_on_select = true; - - show_startup_tips = false; - show_release_notes = false; - - attach_to_session = true; - session_name = "main"; - on_force_close = "detach"; - session_serialization = true; - serialize_pane_viewport = true; - - ui = { - pane_frames = { - hide_session_name = true; - }; - }; - }; - - extraConfig = '' - keybinds { - tab { - bind "n" { NewTab { layout "zoxide-picker"; }; SwitchToMode "Normal"; } - bind "N" { NewTab; SwitchToMode "Normal"; } - } - } - ''; - }; - - programs.eza = { - enable = true; - icons = "always"; - enableBashIntegration = true; - git = true; - extraOptions = [ - "--group-directories-first" - "--all" - ]; - }; - - programs.fzf = { - enable = true; - enableBashIntegration = true; - }; - - programs.zoxide = { - enable = true; - enableBashIntegration = true; - }; - - programs.direnv = { - enable = true; - enableBashIntegration = true; - nix-direnv.enable = true; - }; - - programs.bash = { - enable = true; - enableCompletion = true; - historyControl = [ - "ignoredups" - "erasedups" - ]; - historySize = 50000; - historyFileSize = 100000; - shellOptions = [ - "histappend" - "checkwinsize" - "globstar" - ]; - shellAliases = { - "dr" = "direnv reload"; - "da" = "direnv allow"; - "nfu" = "nix flake update"; - "o" = "opencode"; - ".." = "z .."; - j = "jj"; - jgf = "jj git fetch"; - jgp = "jj git push"; - jgc = "jj git clone --colocate"; - jbs = "jj bookmark set"; - jd = "jj describe"; - js = "jj show"; - jss = "jj show -s"; - jab = "jj abandon"; - jsp = "jj split"; - je = "jj edit --ignore-immutable"; - jall = "jj log -r 'all()'"; - jn = "jj new"; - jdiff = "jj diff"; - jsq = "jj squash"; - h = "hx"; - t = "trash"; - vanity = "mkp224o-amd64-64-24k -d noisebridgevanitytor noisebridge{2..7}"; - }; - initExtra = '' - # Automatically list directory contents when changing directories - auto_l_on_cd() { - if [ "$__LAST_PWD" != "$PWD" ]; then - l - __LAST_PWD="$PWD" - fi - } - - export PROMPT_COMMAND="auto_l_on_cd; $PROMPT_COMMAND" - __LAST_PWD="$PWD" - ''; - }; - - programs.kitty = { - enable = true; - settings = { - hide_window_decorations = "yes"; - draw_minimal_borders = "yes"; - font_family = "CommitMono Nerd Font"; - font_size = "12"; - confirm_os_window_close = "0"; - enable_audio_bell = "no"; - }; - themeFile = "GitHub_Dark_High_Contrast"; - }; - - programs.jujutsu = { - enable = true; - settings = { - remotes.origin.auto-track-bookmarks = "glob:*"; - user = { - inherit email name; - }; - - signing = { - behavior = "own"; - backend = "ssh"; - key = sshSigningKey; - }; - - git = { - sign-on-push = true; - }; - ui = { - default-command = "log"; - editor = "hx"; - pager = "bat --style=plain"; - }; - diff.tool = [ - "difft" - "--color=always" - "$left" - "$right" - ]; - }; - }; - - # Configure Zen Browser with about:config settings - programs.zen-browser = { - enable = true; - policies = { - DisableTelemetry = true; - DisableFirefoxStudies = true; - DisablePocket = true; - DontCheckDefaultBrowser = true; - DisableAppUpdate = true; - DisableMasterPasswordCreation = true; - DisablePasswordReveal = true; - DisableProfileImport = true; - ExtensionUpdate = false; - OfferToSaveLogins = false; - DisableFirefoxAccounts = true; - DisableFormHistory = true; - DisableSafeMode = true; - DisableSetDesktopBackground = true; - EnableTrackingProtection = { - Value = true; - Locked = true; - Cryptomining = true; - Fingerprinting = true; - }; - HardwareAcceleration = true; - NoDefaultBookmarks = true; - PasswordManagerEnabled = false; - Preferences = { - "zen.theme.border-radius" = 0; - "zen.theme.content-element-separation" = 0; - }; - }; - profiles.default = { - isDefault = true; - settings = { - "identity.fxaccounts.enabled" = false; - "ui.prefersReducedMotion" = 1; - }; - extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ - ublock-origin - onepassword-password-manager - sponsorblock - darkreader - vimium - return-youtube-dislikes - react-devtools - firefox-color - pay-by-privacy - translate-web-pages - user-agent-string-switcher - copy-selected-tabs-to-clipboard - dearrow - violentmonkey - tst-indent-line - ]; - search = { - default = "SearXNG"; - privateDefault = "SearXNG"; - force = true; - engines = { - "SearXNG" = { - urls = [ { template = "https://search.extremist.software/search?q={searchTerms}"; } ]; - definedAliases = [ "@s" ]; - }; - }; - }; - }; - }; - - # Override the Kitty desktop entry to always launch in fullscreen - xdg.desktopEntries.kitty = { - name = "Kitty"; - genericName = "Terminal Emulator"; - exec = "${pkgs.kitty}/bin/kitty --start-as=fullscreen ${zellijPersistentSession}/bin/zellij-persistent-session"; - icon = "kitty"; - type = "Application"; - categories = [ - "System" - "TerminalEmulator" - ]; - comment = "Fast, featureful, GPU based terminal emulator"; - }; - - # Extract archives on double-click - xdg.desktopEntries.extract-here = { - name = "Extract Here"; - exec = "file-roller --extract-here %U"; - icon = "file-roller"; - type = "Application"; - categories = [ "Utility" ]; - mimeType = [ - "application/zip" - "application/x-tar" - "application/x-compressed-tar" - "application/x-bzip-compressed-tar" - "application/x-xz-compressed-tar" - "application/x-zstd-compressed-tar" - "application/gzip" - "application/x-7z-compressed" - "application/x-rar" - "application/x-rar-compressed" - ]; - noDisplay = true; - }; - - # Autostart applications using proper desktop files - xdg.autostart = { - enable = true; - entries = [ - "${zenStartup}/share/applications/zen-startup.desktop" - "${kittyZellijStartup}/share/applications/kitty-zellij-startup.desktop" - "${signalStartup}/share/applications/signal-startup.desktop" - "${pkgs.slack}/share/applications/slack.desktop" - "${betterbirdStartup}/share/applications/betterbird-startup.desktop" - "${vesktopStartup}/share/applications/vesktop-startup.desktop" - "${zulipStartup}/share/applications/zulip-startup.desktop" - ]; - }; - - home.file.".local/share/gnome-shell/extensions/tailscale-gnome-qs@tailscale-qs.github.io" = { - source = "${tailscaleQsExtension}/share/gnome-shell/extensions/tailscale-gnome-qs@tailscale-qs.github.io"; - recursive = true; - }; - - systemd.user.services.nasa-apod-wallpaper = { - Unit = { - Description = "Fetch NASA APOD wallpaper"; - After = [ "graphical-session.target" ]; - PartOf = [ "graphical-session.target" ]; - }; - Service = { - Type = "oneshot"; - ExecStart = "${nasaApodWallpaper}/bin/nasa-apod-wallpaper"; - }; - }; - - systemd.user.timers.nasa-apod-wallpaper = { - Unit.Description = "Refresh NASA APOD wallpaper regularly"; - Timer = { - OnStartupSec = "2m"; - OnCalendar = "hourly"; - Persistent = true; - Unit = "nasa-apod-wallpaper.service"; - }; - Install.WantedBy = [ "timers.target" ]; - }; - - # Set Zen Browser as default - xdg.mimeApps = { - enable = true; - defaultApplications = { - "text/html" = "zen-beta.desktop"; - "x-scheme-handler/http" = "zen-beta.desktop"; - "x-scheme-handler/https" = "zen-beta.desktop"; - "x-scheme-handler/about" = "zen-beta.desktop"; - "x-scheme-handler/unknown" = "zen-beta.desktop"; - "x-scheme-handler/mailto" = "eu.betterbird.Betterbird.desktop"; - "inode/directory" = "org.gnome.Nautilus.desktop"; - "application/zip" = "org.gnome.FileRoller.desktop"; - "application/x-tar" = "org.gnome.FileRoller.desktop"; - "application/x-compressed-tar" = "org.gnome.FileRoller.desktop"; - "application/x-bzip-compressed-tar" = "org.gnome.FileRoller.desktop"; - "application/x-xz-compressed-tar" = "org.gnome.FileRoller.desktop"; - "application/x-zstd-compressed-tar" = "org.gnome.FileRoller.desktop"; - "application/gzip" = "org.gnome.FileRoller.desktop"; - "application/x-7z-compressed" = "org.gnome.FileRoller.desktop"; - "application/x-rar" = "org.gnome.FileRoller.desktop"; - "application/x-rar-compressed" = "org.gnome.FileRoller.desktop"; - }; - }; - - home.file.".agents/skills/check-pr".source = "${greptileSkills}/check-pr"; - home.file.".agents/skills/greploop".source = "${greptileSkills}/greploop"; - - home.file.".config/opencode/opencode.json".text = builtins.toJSON { - "$schema" = "https://opencode.ai/config.json"; - autoupdate = false; - plugin = [ "opencode-with-claude" ]; - permission = { - "*" = "allow"; - external_directory = "allow"; - doom_loop = "allow"; - }; - mcp.linear = { - type = "remote"; - url = "https://mcp.linear.app/mcp"; - enabled = true; - }; - model = "openai/gpt-5.4-fast"; - small_model = "openai/gpt-5.4-mini-fast"; - provider.openai.models."gpt-5.4-fast".options = { - reasoningEffort = "xhigh"; - }; - share = "disabled"; - }; - - home.file.".config/opencode/AGENTS.md".text = '' - # NixOS Rules - - - This machine is NixOS. Prefer the Nix way for installing and running tools. - - Do not suggest `apt`, `dnf`, `pacman`, `brew`, `npm -g`, `pip install`, `cargo install`, `curl | sh`, or manual installers unless explicitly asked. - - If a repo has `flake.nix`, treat it as the source of truth for project tooling. - - If a needed tool belongs to the project, add it to `flake.nix` or the dev shell instead of installing it another way. - - If a repo has `flake.nix`, ensure `.envrc` contains `use flake` unless the repo intentionally uses a different setup. - - If there is no `flake.nix` and the tool is only needed temporarily, prefer `nix shell nixpkgs# -c `. - - For persistent tools, prefer declarative Nix configuration. - - Prefer `direnv` or `nix develop` before deciding a tool is missing. - - Never run `nixos-rebuild`, `nh os switch`, `nhs`, or other system switch commands unless explicitly asked. - ''; - - home.file.".config/opencode/tui.json".text = builtins.toJSON { - "$schema" = "https://opencode.ai/tui.json"; - keybinds = { - leader = "ctrl+x"; - command_list = "p"; - variant_cycle = "t"; - }; - }; - - xdg.userDirs = { - enable = true; - setSessionVariables = true; - }; - - gtk = { - enable = true; - gtk4.theme = config.gtk.theme; - gtk3.extraConfig.gtk-application-prefer-dark-theme = 1; - }; - + imports = [ ./home-modules ]; } diff --git a/hosts/framework-work/default.nix b/hosts/framework-work/default.nix new file mode 100644 index 0000000..77c6b12 --- /dev/null +++ b/hosts/framework-work/default.nix @@ -0,0 +1,21 @@ +{ ... }: + +{ + imports = [ + ../../configuration.nix + ./hardware-configuration.nix + ]; + + networking.hostName = "framework-work"; + + fileSystems."/tmp" = { + device = "tmpfs"; + fsType = "tmpfs"; + options = [ + "size=8G" + "mode=1777" + "nosuid" + "nodev" + ]; + }; +} diff --git a/hosts/framework-work/hardware-configuration.nix b/hosts/framework-work/hardware-configuration.nix new file mode 100644 index 0000000..bea50bd --- /dev/null +++ b/hosts/framework-work/hardware-configuration.nix @@ -0,0 +1,47 @@ +{ + config, + lib, + pkgs, + modulesPath, + ... +}: + +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd.availableKernelModules = [ + "nvme" + "xhci_pci" + "thunderbolt" + "usb_storage" + "sd_mod" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/dfaa8624-c14f-4a72-8c5a-193d8294e5cb"; + fsType = "ext4"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/D21C-F860"; + fsType = "vfat"; + options = [ + "fmask=0077" + "dmask=0077" + ]; + }; + + swapDevices = [ + { + device = "/dev/disk/by-uuid/bd8d2630-7c9b-4ec1-8a00-b1a801003732"; + } + ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/framework/default.nix b/hosts/framework/default.nix new file mode 100644 index 0000000..35aa004 --- /dev/null +++ b/hosts/framework/default.nix @@ -0,0 +1,21 @@ +{ ... }: + +{ + imports = [ + ../../configuration.nix + ./hardware-configuration.nix + ]; + + networking.hostName = "framework"; + + fileSystems."/tmp" = { + device = "tmpfs"; + fsType = "tmpfs"; + options = [ + "size=32G" + "mode=1777" + "nosuid" + "nodev" + ]; + }; +} diff --git a/hardware-configuration.nix b/hosts/framework/hardware-configuration.nix similarity index 70% rename from hardware-configuration.nix rename to hosts/framework/hardware-configuration.nix index 20e36c0..e9c22dc 100644 --- a/hardware-configuration.nix +++ b/hosts/framework/hardware-configuration.nix @@ -42,12 +42,7 @@ swapDevices = [ ]; - # Enables DHCP on each ethernet and wireless interface. In case of scripted networking - # (the default) this is the recommended approach. When using systemd-networkd it's - # still possible to use this option, but it's recommended to use it in conjunction - # with explicit per-interface declarations with `networking.interfaces..useDHCP`. networking.useDHCP = lib.mkDefault true; - # networking.interfaces.wlp192s0.useDHCP = lib.mkDefault true; hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; }