From 829fde6a3af7ba20959d90911e07dde5167b3f4b Mon Sep 17 00:00:00 2001 From: William Date: Sat, 7 Feb 2026 07:54:49 -0300 Subject: [PATCH] packages: convert to self-contained flake-parts modules Each package file now exports its own perSystem.packages. definition instead of being called from a centralized packages.nix file. Co-Authored-By: Claude Opus 4.5 --- packages/base16-schemes.nix | 55 ++--- packages/claude-desktop.nix | 400 +++++++++++++++++------------------ packages/fastfetch.nix | 159 +++++++------- packages/hm-cli.nix | 176 +++++++-------- packages/kwrite.nix | 32 +-- packages/toggleaudiosink.nix | 82 +++---- 6 files changed, 459 insertions(+), 445 deletions(-) diff --git a/packages/base16-schemes.nix b/packages/base16-schemes.nix index ffd6c04..fbb1341 100644 --- a/packages/base16-schemes.nix +++ b/packages/base16-schemes.nix @@ -1,32 +1,35 @@ +{ ... }: + { - lib, - stdenv, - fetchFromGitHub, -}: -stdenv.mkDerivation (finalAttrs: { - pname = "base16-schemes"; - version = "0-unstable-2025-06-04"; + perSystem = + { pkgs, ... }: + { + packages.base16-schemes = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "base16-schemes"; + version = "0-unstable-2025-06-04"; - src = fetchFromGitHub { - owner = "tinted-theming"; - repo = "schemes"; - rev = "317a5e10c35825a6c905d912e480dfe8e71c7559"; - hash = "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM="; - }; + src = pkgs.fetchFromGitHub { + owner = "tinted-theming"; + repo = "schemes"; + rev = "317a5e10c35825a6c905d912e480dfe8e71c7559"; + hash = "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM="; + }; - installPhase = '' - runHook preInstall + installPhase = '' + runHook preInstall - mkdir -p $out/share/themes/ - install base16/*.yaml $out/share/themes/ + mkdir -p $out/share/themes/ + install base16/*.yaml $out/share/themes/ - runHook postInstall - ''; + runHook postInstall + ''; - meta = { - description = "All the color schemes for use in base16 packages"; - homepage = "https://github.com/tinted-theming/schemes"; - maintainers = [ lib.maintainers.DamienCassou ]; - license = lib.licenses.mit; - }; -}) + meta = { + description = "All the color schemes for use in base16 packages"; + homepage = "https://github.com/tinted-theming/schemes"; + maintainers = [ pkgs.lib.maintainers.DamienCassou ]; + license = pkgs.lib.licenses.mit; + }; + }); + }; +} diff --git a/packages/claude-desktop.nix b/packages/claude-desktop.nix index e72fc37..e93f3af 100644 --- a/packages/claude-desktop.nix +++ b/packages/claude-desktop.nix @@ -1,221 +1,215 @@ +{ inputs, ... }: + { - lib, - stdenv, - fetchurl, - makeWrapper, - makeDesktopItem, - copyDesktopItems, - p7zip, - unzip, - electron, - nodejs, - asar, - graphicsmagick, -}: + perSystem = + { system, ... }: + let + pkgs = import inputs.nixpkgs { + inherit system; + config.allowUnfree = true; + }; -let - pname = "claude-desktop"; - version = "1.0.1768"; # Updated based on extracted nupkg + pname = "claude-desktop"; + version = "1.0.1768"; - srcs.x86_64-linux = fetchurl { - url = "https://downloads.claude.ai/releases/win32/x64/1.0.1768/Claude-67d01376d0e9d08b328455f6db9e63b0d603506a.exe"; - hash = "sha256-x76Qav38ya3ObpWIq3dDowo79LgvVquMfaZeH8M1LUk=;"; - }; + srcs.x86_64-linux = pkgs.fetchurl { + url = "https://downloads.claude.ai/releases/win32/x64/1.0.1768/Claude-67d01376d0e9d08b328455f6db9e63b0d603506a.exe"; + hash = "sha256-x76Qav38ya3ObpWIq3dDowo79LgvVquMfaZeH8M1LUk=;"; + }; - src = - srcs.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}"); + src = + srcs.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system: ${pkgs.stdenv.hostPlatform.system}"); - # Stub implementation for claude-native module - claudeNativeStub = '' - // Stub implementation of claude-native using KeyboardKey enum values - const KeyboardKey = { - Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40, - CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250, - End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262, - DownArrow: 81, Delete: 79, Meta: 187 - }; - Object.freeze(KeyboardKey); - module.exports = { - getWindowsVersion: () => "10.0.0", - setWindowEffect: () => {}, - removeWindowEffect: () => {}, - getIsMaximized: () => false, - flashFrame: () => {}, - clearFlashFrame: () => {}, - showNotification: () => {}, - setProgressBar: () => {}, - clearProgressBar: () => {}, - setOverlayIcon: () => {}, - clearOverlayIcon: () => {}, - KeyboardKey - }; - ''; + claudeNativeStub = '' + // Stub implementation of claude-native using KeyboardKey enum values + const KeyboardKey = { + Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40, + CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250, + End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262, + DownArrow: 81, Delete: 79, Meta: 187 + }; + Object.freeze(KeyboardKey); + module.exports = { + getWindowsVersion: () => "10.0.0", + setWindowEffect: () => {}, + removeWindowEffect: () => {}, + getIsMaximized: () => false, + flashFrame: () => {}, + clearFlashFrame: () => {}, + showNotification: () => {}, + setProgressBar: () => {}, + clearProgressBar: () => {}, + setOverlayIcon: () => {}, + clearOverlayIcon: () => {}, + KeyboardKey + }; + ''; + in + { + packages.claude-desktop = pkgs.stdenv.mkDerivation rec { + inherit pname version src; -in -stdenv.mkDerivation rec { - inherit pname version src; + nativeBuildInputs = with pkgs; [ + makeWrapper + copyDesktopItems + p7zip + unzip + nodejs + graphicsmagick + ]; - nativeBuildInputs = [ - makeWrapper - copyDesktopItems - p7zip - unzip - nodejs - graphicsmagick - ]; + buildInputs = [ pkgs.electron ]; - buildInputs = [ - electron - ]; + desktopItems = [ + (pkgs.makeDesktopItem { + name = "claude-desktop"; + desktopName = "Claude"; + comment = "AI assistant from Anthropic"; + exec = "claude-desktop %u"; + icon = "claude-desktop"; + categories = [ + "Network" + "Chat" + "Office" + ]; + mimeTypes = [ "x-scheme-handler/claude" ]; + startupNotify = true; + startupWMClass = "Claude"; + }) + ]; - desktopItems = [ - (makeDesktopItem { - name = "claude-desktop"; - desktopName = "Claude"; - comment = "AI assistant from Anthropic"; - exec = "claude-desktop %u"; - icon = "claude-desktop"; - categories = [ - "Network" - "Chat" - "Office" - ]; - mimeTypes = [ "x-scheme-handler/claude" ]; - startupNotify = true; - startupWMClass = "Claude"; - }) - ]; + unpackPhase = '' + runHook preUnpack - unpackPhase = '' - runHook preUnpack + # Extract the Windows installer - use -y to auto-overwrite + 7z x -y $src -o./extracted - # Extract the Windows installer - use -y to auto-overwrite - 7z x -y $src -o./extracted + # The installer contains a NuGet package + if [ -f ./extracted/AnthropicClaude-*-full.nupkg ]; then + echo "Found NuGet package, extracting..." + # NuGet packages are just zip files + unzip -q ./extracted/AnthropicClaude-*-full.nupkg -d ./nupkg - # The installer contains a NuGet package - if [ -f ./extracted/AnthropicClaude-*-full.nupkg ]; then - echo "Found NuGet package, extracting..." - # NuGet packages are just zip files - unzip -q ./extracted/AnthropicClaude-*-full.nupkg -d ./nupkg + # Extract app.asar to modify it + if [ -f ./nupkg/lib/net45/resources/app.asar ]; then + echo "Extracting app.asar..." + ${pkgs.asar}/bin/asar extract ./nupkg/lib/net45/resources/app.asar ./app - # Extract app.asar to modify it - if [ -f ./nupkg/lib/net45/resources/app.asar ]; then - echo "Extracting app.asar..." - ${asar}/bin/asar extract ./nupkg/lib/net45/resources/app.asar ./app + # Also copy the unpacked resources + if [ -d ./nupkg/lib/net45/resources/app.asar.unpacked ]; then + cp -r ./nupkg/lib/net45/resources/app.asar.unpacked/* ./app/ + fi - # Also copy the unpacked resources - if [ -d ./nupkg/lib/net45/resources/app.asar.unpacked ]; then - cp -r ./nupkg/lib/net45/resources/app.asar.unpacked/* ./app/ - fi - - # Copy additional resources - mkdir -p ./app/resources - mkdir -p ./app/resources/i18n - cp ./nupkg/lib/net45/resources/Tray* ./app/resources/ || true - cp ./nupkg/lib/net45/resources/*-*.json ./app/resources/i18n/ || true - fi - else - echo "NuGet package not found" - ls -la ./extracted/ - exit 1 - fi - - runHook postUnpack - ''; - - buildPhase = '' - runHook preBuild - - # Replace the Windows-specific claude-native module with a stub - if [ -d ./app/node_modules/claude-native ]; then - echo "Replacing claude-native module with Linux stub..." - rm -rf ./app/node_modules/claude-native/*.node - cat > ./app/node_modules/claude-native/index.js << 'EOF' - ${claudeNativeStub} - EOF - fi - - # Fix the title bar detection (from aaddrick script) - echo "Fixing title bar detection..." - SEARCH_BASE="./app/.vite/renderer/main_window/assets" - if [ -d "$SEARCH_BASE" ]; then - TARGET_FILE=$(find "$SEARCH_BASE" -type f -name "MainWindowPage-*.js" | head -1) - if [ -n "$TARGET_FILE" ]; then - echo "Found target file: $TARGET_FILE" - # Replace patterns like 'if(!VAR1 && VAR2)' with 'if(VAR1 && VAR2)' - sed -i -E 's/if\(!([a-zA-Z]+)[[:space:]]*&&[[:space:]]*([a-zA-Z]+)\)/if(\1 \&\& \2)/g' "$TARGET_FILE" - echo "Title bar fix applied" - fi - fi - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - mkdir -p $out/lib/claude-desktop - - # Repack the modified app as app.asar - cd ./app - ${asar}/bin/asar pack . ../app.asar - cd .. - - # Copy resources - mkdir -p $out/lib/claude-desktop/resources - cp ./app.asar $out/lib/claude-desktop/resources/ - - # Create app.asar.unpacked directory with the stub - mkdir -p $out/lib/claude-desktop/resources/app.asar.unpacked/node_modules/claude-native - cat > $out/lib/claude-desktop/resources/app.asar.unpacked/node_modules/claude-native/index.js << 'EOF' - ${claudeNativeStub} - EOF - - # Copy other resources - if [ -d ./nupkg/lib/net45/resources ]; then - cp ./nupkg/lib/net45/resources/*.png $out/lib/claude-desktop/resources/ 2>/dev/null || true - cp ./nupkg/lib/net45/resources/*.ico $out/lib/claude-desktop/resources/ 2>/dev/null || true - cp ./nupkg/lib/net45/resources/*.json $out/lib/claude-desktop/resources/ 2>/dev/null || true - fi - - # Create wrapper script - makeWrapper ${electron}/bin/electron $out/bin/claude-desktop \ - --add-flags "$out/lib/claude-desktop/resources/app.asar" \ - --set DISABLE_AUTOUPDATER 1 \ - --set NODE_ENV production - - # Extract and install icons in multiple sizes - if [ -f ./extracted/setupIcon.ico ]; then - echo "Converting and installing icons..." - # Count frames in the ICO file and extract each one - frame_count=$(gm identify ./extracted/setupIcon.ico | wc -l) - for i in $(seq 0 $((frame_count - 1))); do - gm convert "./extracted/setupIcon.ico[$i]" "./extracted/setupIcon-$i.png" 2>/dev/null || true - done - - # Loop through converted icons and install them by size - for img in ./extracted/setupIcon-*.png; do - if [ -f "$img" ]; then - size=$(gm identify -format "%wx%h" "$img") - # Skip smallest icons (16x16 and 32x32) as they're too low quality - if [ "$size" != "16x16" ] && [ "$size" != "32x32" ]; then - mkdir -p "$out/share/icons/hicolor/$size/apps" - cp "$img" "$out/share/icons/hicolor/$size/apps/claude-desktop.png" + # Copy additional resources + mkdir -p ./app/resources + mkdir -p ./app/resources/i18n + cp ./nupkg/lib/net45/resources/Tray* ./app/resources/ || true + cp ./nupkg/lib/net45/resources/*-*.json ./app/resources/i18n/ || true + fi + else + echo "NuGet package not found" + ls -la ./extracted/ + exit 1 fi - fi - done - fi - runHook postInstall - ''; + runHook postUnpack + ''; - meta = with lib; { - description = "Claude Desktop - AI assistant from Anthropic"; - homepage = "https://claude.ai"; - license = licenses.unfree; - sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; - maintainers = with maintainers; [ ]; - platforms = [ "x86_64-linux" ]; - mainProgram = "claude-desktop"; - }; + buildPhase = '' + runHook preBuild + + # Replace the Windows-specific claude-native module with a stub + if [ -d ./app/node_modules/claude-native ]; then + echo "Replacing claude-native module with Linux stub..." + rm -rf ./app/node_modules/claude-native/*.node + cat > ./app/node_modules/claude-native/index.js << 'EOF' + ${claudeNativeStub} + EOF + fi + + # Fix the title bar detection (from aaddrick script) + echo "Fixing title bar detection..." + SEARCH_BASE="./app/.vite/renderer/main_window/assets" + if [ -d "$SEARCH_BASE" ]; then + TARGET_FILE=$(find "$SEARCH_BASE" -type f -name "MainWindowPage-*.js" | head -1) + if [ -n "$TARGET_FILE" ]; then + echo "Found target file: $TARGET_FILE" + # Replace patterns like 'if(!VAR1 && VAR2)' with 'if(VAR1 && VAR2)' + sed -i -E 's/if\(!([a-zA-Z]+)[[:space:]]*&&[[:space:]]*([a-zA-Z]+)\)/if(\1 \&\& \2)/g' "$TARGET_FILE" + echo "Title bar fix applied" + fi + fi + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/lib/claude-desktop + + # Repack the modified app as app.asar + cd ./app + ${pkgs.asar}/bin/asar pack . ../app.asar + cd .. + + # Copy resources + mkdir -p $out/lib/claude-desktop/resources + cp ./app.asar $out/lib/claude-desktop/resources/ + + # Create app.asar.unpacked directory with the stub + mkdir -p $out/lib/claude-desktop/resources/app.asar.unpacked/node_modules/claude-native + cat > $out/lib/claude-desktop/resources/app.asar.unpacked/node_modules/claude-native/index.js << 'EOF' + ${claudeNativeStub} + EOF + + # Copy other resources + if [ -d ./nupkg/lib/net45/resources ]; then + cp ./nupkg/lib/net45/resources/*.png $out/lib/claude-desktop/resources/ 2>/dev/null || true + cp ./nupkg/lib/net45/resources/*.ico $out/lib/claude-desktop/resources/ 2>/dev/null || true + cp ./nupkg/lib/net45/resources/*.json $out/lib/claude-desktop/resources/ 2>/dev/null || true + fi + + # Create wrapper script + makeWrapper ${pkgs.electron}/bin/electron $out/bin/claude-desktop \ + --add-flags "$out/lib/claude-desktop/resources/app.asar" \ + --set DISABLE_AUTOUPDATER 1 \ + --set NODE_ENV production + + # Extract and install icons in multiple sizes + if [ -f ./extracted/setupIcon.ico ]; then + echo "Converting and installing icons..." + # Count frames in the ICO file and extract each one + frame_count=$(gm identify ./extracted/setupIcon.ico | wc -l) + for i in $(seq 0 $((frame_count - 1))); do + gm convert "./extracted/setupIcon.ico[$i]" "./extracted/setupIcon-$i.png" 2>/dev/null || true + done + + # Loop through converted icons and install them by size + for img in ./extracted/setupIcon-*.png; do + if [ -f "$img" ]; then + size=$(gm identify -format "%wx%h" "$img") + # Skip smallest icons (16x16 and 32x32) as they're too low quality + if [ "$size" != "16x16" ] && [ "$size" != "32x32" ]; then + mkdir -p "$out/share/icons/hicolor/$size/apps" + cp "$img" "$out/share/icons/hicolor/$size/apps/claude-desktop.png" + fi + fi + done + fi + + runHook postInstall + ''; + + meta = with pkgs.lib; { + description = "Claude Desktop - AI assistant from Anthropic"; + homepage = "https://claude.ai"; + license = licenses.unfree; + sourceProvenance = with sourceTypes; [ binaryNativeCode ]; + maintainers = [ ]; + platforms = [ "x86_64-linux" ]; + mainProgram = "claude-desktop"; + }; + }; + }; } diff --git a/packages/fastfetch.nix b/packages/fastfetch.nix index fa8e0ea..aa8d616 100644 --- a/packages/fastfetch.nix +++ b/packages/fastfetch.nix @@ -1,81 +1,84 @@ +{ ... }: + { - lib, - pkgs ? import { }, -}: + perSystem = + { pkgs, lib, ... }: + let + fastfetch-logo = pkgs.fetchurl { + url = "https://discourse.nixos.org/uploads/default/original/3X/3/6/36954e6d6aa32c8b00f50ca43f142d898c1ff535.png"; + hash = "sha256-aLHz8jSAFocrn+Pb4vRq0wtkYFJpBpZRevd+VoZC/PQ="; + }; -let - fastfetch-logo = pkgs.fetchurl { - url = "https://discourse.nixos.org/uploads/default/original/3X/3/6/36954e6d6aa32c8b00f50ca43f142d898c1ff535.png"; - hash = "sha256-aLHz8jSAFocrn+Pb4vRq0wtkYFJpBpZRevd+VoZC/PQ="; - }; - - fastfetch-config = pkgs.writeText "fastfetch-config.json" ( - builtins.toJSON { - "$schema" = "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json"; - modules = [ - "title" - "separator" - { - type = "os"; - keyWidth = 9; + fastfetch-config = pkgs.writeText "fastfetch-config.json" ( + builtins.toJSON { + "$schema" = "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json"; + modules = [ + "title" + "separator" + { + type = "os"; + keyWidth = 9; + } + { + type = "kernel"; + keyWidth = 9; + } + { + type = "uptime"; + keyWidth = 9; + } + { + type = "shell"; + keyWidth = 9; + } + "break" + { + type = "cpu"; + keyWidth = 11; + } + { + type = "memory"; + keyWidth = 11; + } + { + type = "swap"; + keyWidth = 11; + } + { + type = "disk"; + folders = "/"; + keyWidth = 11; + } + { + type = "command"; + key = "Systemd"; + keyWidth = 11; + text = "echo \"$(systemctl list-units --state=failed --no-legend | wc -l) failed units, $(systemctl list-jobs --no-legend | wc -l) queued jobs\""; + } + "break" + { + type = "command"; + key = "Public IP"; + keyWidth = 15; + text = "curl -s -4 ifconfig.me 2>/dev/null || echo 'N/A'"; + } + { + type = "command"; + key = "Tailscale IP"; + keyWidth = 15; + text = "tailscale ip -4 2>/dev/null || echo 'N/A'"; + } + { + type = "command"; + key = "Local IP"; + keyWidth = 15; + text = "ip -4 addr show scope global | grep inet | head -n1 | awk '{print $2}' | cut -d/ -f1"; + } + ]; } - { - type = "kernel"; - keyWidth = 9; - } - { - type = "uptime"; - keyWidth = 9; - } - { - type = "shell"; - keyWidth = 9; - } - "break" - { - type = "cpu"; - keyWidth = 11; - } - { - type = "memory"; - keyWidth = 11; - } - { - type = "swap"; - keyWidth = 11; - } - { - type = "disk"; - folders = "/"; - keyWidth = 11; - } - { - type = "command"; - key = "Systemd"; - keyWidth = 11; - text = "echo \"$(systemctl list-units --state=failed --no-legend | wc -l) failed units, $(systemctl list-jobs --no-legend | wc -l) queued jobs\""; - } - "break" - { - type = "command"; - key = "Public IP"; - keyWidth = 15; - text = "curl -s -4 ifconfig.me 2>/dev/null || echo 'N/A'"; - } - { - type = "command"; - key = "Tailscale IP"; - keyWidth = 15; - text = "tailscale ip -4 2>/dev/null || echo 'N/A'"; - } - { - type = "command"; - key = "Local IP"; - keyWidth = 15; - text = "ip -4 addr show scope global | grep inet | head -n1 | awk '{print $2}' | cut -d/ -f1"; - } - ]; - } - ); -in -pkgs.writeShellScriptBin "fastfetch" ''exec ${lib.getExe pkgs.fastfetch} --config ${fastfetch-config} --logo-type kitty --logo ${fastfetch-logo} --logo-padding-right 1 --logo-width 36 "$@" '' + ); + in + { + packages.fastfetch = pkgs.writeShellScriptBin "fastfetch" ''exec ${lib.getExe pkgs.fastfetch} --config ${fastfetch-config} --logo-type kitty --logo ${fastfetch-logo} --logo-padding-right 1 --logo-width 36 "$@" ''; + }; +} diff --git a/packages/hm-cli.nix b/packages/hm-cli.nix index f6034ea..94dae66 100644 --- a/packages/hm-cli.nix +++ b/packages/hm-cli.nix @@ -1,105 +1,109 @@ +{ ... }: + { - pkgs ? import { }, -}: + perSystem = + { pkgs, ... }: + { + packages.hm-cli = pkgs.writeShellScriptBin "hm" '' + set -e -pkgs.writeShellScriptBin "hm" '' - set -e + HM="${pkgs.lib.getExe pkgs.home-manager}" + FLAKE_PATH="''${HM_PATH:-$HOME/.config/home-manager}" + FLAKE_OUTPUT="''${HM_USER:-$(whoami)@$(hostname)}" - HM="${pkgs.lib.getExe pkgs.home-manager}" - FLAKE_PATH="''${HM_PATH:-$HOME/.config/home-manager}" - FLAKE_OUTPUT="''${HM_USER:-$(whoami)@$(hostname)}" + show_usage() { + cat < [args] - show_usage() { - cat < [args] + Commands: + apply Switch to a new generation + generation list List all generations + generation delete ID... Delete specified generation(s) + generation rollback Rollback to the previous generation + generation switch ID Switch to the specified generation + generation cleanup Delete all but the current generation - Commands: - apply Switch to a new generation - generation list List all generations - generation delete ID... Delete specified generation(s) - generation rollback Rollback to the previous generation - generation switch ID Switch to the specified generation - generation cleanup Delete all but the current generation + Environment Variables: + HM_PATH Override default flake path (~/.config/home-manager) + Currently set to "''${HM_PATH:-}" + HM_USER Override default user output ("$(whoami)@$(hostname)") + Currently set to "''${HM_USER:-}" + EOF + } - Environment Variables: - HM_PATH Override default flake path (~/.config/home-manager) - Currently set to "''${HM_PATH:-}" - HM_USER Override default user output ("$(whoami)@$(hostname)") - Currently set to "''${HM_USER:-}" - EOF - } - - if [[ $# -eq 0 ]]; then - show_usage - exit 1 - fi - - case "$1" in - apply) - "$HM" switch --flake "$FLAKE_PATH#$FLAKE_OUTPUT" -b bkp - ;; - generation) - if [[ $# -lt 2 ]]; then - echo "Error: generation command requires a subcommand" + if [[ $# -eq 0 ]]; then show_usage exit 1 fi - case "$2" in - list) - "$HM" generations + case "$1" in + apply) + "$HM" switch --flake "$FLAKE_PATH#$FLAKE_OUTPUT" -b bkp ;; - delete) - if [[ $# -lt 3 ]]; then - echo "Error: delete requires at least one generation ID" + generation) + if [[ $# -lt 2 ]]; then + echo "Error: generation command requires a subcommand" + show_usage exit 1 fi - shift 2 - "$HM" remove-generations "$@" - ;; - rollback) - PREV_GEN=$("$HM" generations | \ - sed -n 's/^[[:space:]]*id \([0-9]\+\).*/\1/p' | \ - head -n 2 | tail -n 1) - if [[ -z "$PREV_GEN" ]]; then - echo "Error: could not determine previous generation (possibly only one generation exists)" - exit 1 - fi - "$HM" switch --flake "$FLAKE_PATH" --switch-generation "$PREV_GEN" -b bkp - ;; - switch) - if [[ $# -ne 3 ]]; then - echo "Error: switch requires exactly one generation ID" - exit 1 - fi - "$HM" switch --flake "$FLAKE_PATH" --switch-generation "$3" -b bkp - ;; - cleanup) - CURRENT_GEN=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .* (current)$/\1/p') - if [[ -z "$CURRENT_GEN" ]]; then - echo "Error: could not determine current generation" - exit 1 - fi - OLD_GENS=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .*/\1/p' | grep -v "^$CURRENT_GEN$") - if [[ -z "$OLD_GENS" ]]; then - echo "No old generations to delete" - else - echo "Deleting generations: $(echo $OLD_GENS | tr '\n' ' ')" - echo "$OLD_GENS" | xargs "$HM" remove-generations - echo "Cleanup complete. Current generation $CURRENT_GEN preserved." - fi + + case "$2" in + list) + "$HM" generations + ;; + delete) + if [[ $# -lt 3 ]]; then + echo "Error: delete requires at least one generation ID" + exit 1 + fi + shift 2 + "$HM" remove-generations "$@" + ;; + rollback) + PREV_GEN=$("$HM" generations | \ + sed -n 's/^[[:space:]]*id \([0-9]\+\).*/\1/p' | \ + head -n 2 | tail -n 1) + if [[ -z "$PREV_GEN" ]]; then + echo "Error: could not determine previous generation (possibly only one generation exists)" + exit 1 + fi + "$HM" switch --flake "$FLAKE_PATH" --switch-generation "$PREV_GEN" -b bkp + ;; + switch) + if [[ $# -ne 3 ]]; then + echo "Error: switch requires exactly one generation ID" + exit 1 + fi + "$HM" switch --flake "$FLAKE_PATH" --switch-generation "$3" -b bkp + ;; + cleanup) + CURRENT_GEN=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .* (current)$/\1/p') + if [[ -z "$CURRENT_GEN" ]]; then + echo "Error: could not determine current generation" + exit 1 + fi + OLD_GENS=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .*/\1/p' | grep -v "^$CURRENT_GEN$") + if [[ -z "$OLD_GENS" ]]; then + echo "No old generations to delete" + else + echo "Deleting generations: $(echo $OLD_GENS | tr '\n' ' ')" + echo "$OLD_GENS" | xargs "$HM" remove-generations + echo "Cleanup complete. Current generation $CURRENT_GEN preserved." + fi + ;; + *) + echo "Error: unknown generation subcommand '$2'" + show_usage + exit 1 + ;; + esac ;; *) - echo "Error: unknown generation subcommand '$2'" + echo "Error: unknown command '$1'" show_usage exit 1 ;; esac - ;; - *) - echo "Error: unknown command '$1'" - show_usage - exit 1 - ;; - esac -'' + ''; + }; +} diff --git a/packages/kwrite.nix b/packages/kwrite.nix index 14ebce1..67ce69e 100644 --- a/packages/kwrite.nix +++ b/packages/kwrite.nix @@ -1,15 +1,21 @@ -{ pkgs }: +{ ... }: -pkgs.symlinkJoin { - name = "kwrite"; - paths = [ pkgs.kdePackages.kate ]; - postBuild = '' - rm -rf $out/bin/kate \ - $out/bin/.kate-wrapped \ - $out/share/applications/org.kde.kate.desktop \ - $out/share/man \ - $out/share/icons/hicolor/*/apps/kate.png \ - $out/share/icons/hicolor/scalable/apps/kate.svg \ - $out/share/appdata/org.kde.kate.appdata.xml - ''; +{ + perSystem = + { pkgs, ... }: + { + packages.kwrite = pkgs.symlinkJoin { + name = "kwrite"; + paths = [ pkgs.kdePackages.kate ]; + postBuild = '' + rm -rf $out/bin/kate \ + $out/bin/.kate-wrapped \ + $out/share/applications/org.kde.kate.desktop \ + $out/share/man \ + $out/share/icons/hicolor/*/apps/kate.png \ + $out/share/icons/hicolor/scalable/apps/kate.svg \ + $out/share/appdata/org.kde.kate.appdata.xml + ''; + }; + }; } diff --git a/packages/toggleaudiosink.nix b/packages/toggleaudiosink.nix index 623346f..22e0d44 100644 --- a/packages/toggleaudiosink.nix +++ b/packages/toggleaudiosink.nix @@ -1,48 +1,52 @@ +{ ... }: + { - pkgs ? import { }, -}: + perSystem = + { pkgs, ... }: + { + packages.toggleaudiosink = pkgs.writeShellScriptBin "toggleaudiosink" '' + #!/usr/bin/env bash -pkgs.writeShellScriptBin "toggleaudiosink" '' - #!/usr/bin/env bash + sound_server="pipewire" - sound_server="pipewire" + # Grab a count of how many audio sinks we have + sink_count=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -c "Sink #[[:digit:]]") + # Create an array of the actual sink IDs + sinks=() + mapfile -t sinks < <(${pkgs.pulseaudio}/bin/pactl list sinks | grep 'Sink #[[:digit:]]' | sed -n -e 's/.*Sink #\([[:digit:]]\)/\1/p') + # Get the ID of the active sink + active_sink_name=$(${pkgs.pulseaudio}/bin/pactl info | grep 'Default Sink:' | sed -n -e 's/.*Default Sink:[[:space:]]\+\(.*\)/\1/p') + active_sink=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -B 2 "$active_sink_name" | sed -n -e 's/Sink #\([[:digit:]]\)/\1/p' | head -n 1) - # Grab a count of how many audio sinks we have - sink_count=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -c "Sink #[[:digit:]]") - # Create an array of the actual sink IDs - sinks=() - mapfile -t sinks < <(${pkgs.pulseaudio}/bin/pactl list sinks | grep 'Sink #[[:digit:]]' | sed -n -e 's/.*Sink #\([[:digit:]]\)/\1/p') - # Get the ID of the active sink - active_sink_name=$(${pkgs.pulseaudio}/bin/pactl info | grep 'Default Sink:' | sed -n -e 's/.*Default Sink:[[:space:]]\+\(.*\)/\1/p') - active_sink=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -B 2 "$active_sink_name" | sed -n -e 's/Sink #\([[:digit:]]\)/\1/p' | head -n 1) + # Get the ID of the last sink in the array + final_sink=''${sinks[$((sink_count - 1))]} - # Get the ID of the last sink in the array - final_sink=''${sinks[$((sink_count - 1))]} + # Find the index of the active sink + for index in "''${!sinks[@]}"; do + if [[ "''${sinks[$index]}" == "$active_sink" ]]; then + active_sink_index=$index + fi + done - # Find the index of the active sink - for index in "''${!sinks[@]}"; do - if [[ "''${sinks[$index]}" == "$active_sink" ]]; then - active_sink_index=$index - fi - done + # Default to the first sink in the list + next_sink=''${sinks[0]} + next_sink_index=0 - # Default to the first sink in the list - next_sink=''${sinks[0]} - next_sink_index=0 + # If we're not at the end of the list, move up the list + if [[ $active_sink -ne $final_sink ]]; then + next_sink_index=$((active_sink_index + 1)) + next_sink=''${sinks[$next_sink_index]} + fi - # If we're not at the end of the list, move up the list - if [[ $active_sink -ne $final_sink ]]; then - next_sink_index=$((active_sink_index + 1)) - next_sink=''${sinks[$next_sink_index]} - fi + # Change the default sink + # Get the name of the next sink + next_sink_name=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -C 2 "Sink #$next_sink" | sed -n -e 's/.*Name:[[:space:]]\+\(.*\)/\1/p' | head -n 1) + ${pkgs.pulseaudio}/bin/pactl set-default-sink "$next_sink_name" - # Change the default sink - # Get the name of the next sink - next_sink_name=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -C 2 "Sink #$next_sink" | sed -n -e 's/.*Name:[[:space:]]\+\(.*\)/\1/p' | head -n 1) - ${pkgs.pulseaudio}/bin/pactl set-default-sink "$next_sink_name" - - # Move all inputs to the new sink - for app in $(${pkgs.pulseaudio}/bin/pactl list sink-inputs | sed -n -e 's/.*Sink Input #\([[:digit:]]\)/\1/p'); do - ${pkgs.pulseaudio}/bin/pactl "move-sink-input $app $next_sink" - done -'' + # Move all inputs to the new sink + for app in $(${pkgs.pulseaudio}/bin/pactl list sink-inputs | sed -n -e 's/.*Sink Input #\([[:digit:]]\)/\1/p'); do + ${pkgs.pulseaudio}/bin/pactl "move-sink-input $app $next_sink" + done + ''; + }; +}