From 2520b17816a8aa7705234ffc7b64211967605946 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Wed, 10 Sep 2025 16:54:42 -0400 Subject: [PATCH 1/2] feat: remove flake-utils --- README.md | 1 - flake.lock | 34 -------------------- flake.nix | 94 ++++++++++++++++++++++++++++++++---------------------- 3 files changed, 55 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 91c633e..62499b1 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ Add the following to your `flake.nix`: ```nix inputs.claude-desktop.url = "github:k3d3/claude-desktop-linux-flake"; inputs.claude-desktop.inputs.nixpkgs.follows = "nixpkgs"; -inputs.claude-desktop.inputs.flake-utils.follows = "flake-utils"; ``` And then the following package to your `environment.systemPackages` or `home.packages`: diff --git a/flake.lock b/flake.lock index f8553b7..61b8bbb 100644 --- a/flake.lock +++ b/flake.lock @@ -1,23 +1,5 @@ { "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1749174413, @@ -36,24 +18,8 @@ }, "root": { "inputs": { - "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index d1697eb..cbbc900 100644 --- a/flake.nix +++ b/flake.nix @@ -3,47 +3,63 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { - self, - nixpkgs, - flake-utils, - }: - flake-utils.lib.eachSystem ["x86_64-linux" "aarch64-linux"] (system: let - pkgs = import nixpkgs { - inherit system; - config.allowUnfree = true; - }; - in { - packages = rec { - patchy-cnb = pkgs.callPackage ./pkgs/patchy-cnb.nix {}; - claude-desktop = pkgs.callPackage ./pkgs/claude-desktop.nix { - inherit patchy-cnb; - }; - claude-desktop-with-fhs = pkgs.buildFHSEnv { - name = "claude-desktop"; - targetPkgs = pkgs: - with pkgs; [ - docker - glibc - openssl - nodejs - uv + outputs = + inputs: + let + inherit (inputs.nixpkgs) lib; + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + eachSystem = lib.genAttrs systems; + pkgsFor = lib.genAttrs systems ( + system: + import inputs.nixpkgs { + inherit system; + config.allowUnfreePredicate = + pkg: + builtins.elem (lib.getName pkg) [ + "claude-desktop" ]; - runScript = "${claude-desktop}/bin/claude-desktop"; - extraInstallCommands = '' - # Copy desktop file from the claude-desktop package - mkdir -p $out/share/applications - cp ${claude-desktop}/share/applications/claude.desktop $out/share/applications/ + } + ); + in + { + packages = eachSystem ( + system: + let + pkgs = pkgsFor.${system}; + in + rec { + patchy-cnb = pkgs.callPackage ./pkgs/patchy-cnb.nix { }; + claude-desktop = pkgs.callPackage ./pkgs/claude-desktop.nix { + inherit patchy-cnb; + }; + claude-desktop-with-fhs = pkgs.buildFHSEnv { + name = "claude-desktop"; + targetPkgs = + pkgs: with pkgs; [ + docker + glibc + openssl + nodejs + uv + ]; + runScript = "${claude-desktop}/bin/claude-desktop"; + extraInstallCommands = '' + # Copy desktop file from the claude-desktop package + mkdir -p $out/share/applications + cp ${claude-desktop}/share/applications/claude.desktop $out/share/applications/ - # Copy icons - mkdir -p $out/share/icons - cp -r ${claude-desktop}/share/icons/* $out/share/icons/ - ''; - }; - default = claude-desktop; - }; - }); + # Copy icons + mkdir -p $out/share/icons + cp -r ${claude-desktop}/share/icons/* $out/share/icons/ + ''; + }; + default = claude-desktop; + } + ); + }; } From bf8e4afb0e41317377c78ab8510dcd0a6e543bce Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Wed, 10 Sep 2025 16:54:55 -0400 Subject: [PATCH 2/2] nix: format code --- pkgs/claude-desktop.nix | 335 ++++++++++++++++++++-------------------- pkgs/patchy-cnb.nix | 67 ++++---- 2 files changed, 200 insertions(+), 202 deletions(-) diff --git a/pkgs/claude-desktop.nix b/pkgs/claude-desktop.nix index 752cda7..7339228 100644 --- a/pkgs/claude-desktop.nix +++ b/pkgs/claude-desktop.nix @@ -10,8 +10,9 @@ makeDesktopItem, makeWrapper, patchy-cnb, - perl -}: let + perl, +}: +let pname = "claude-desktop"; version = "0.12.129"; srcExe = fetchurl { @@ -20,171 +21,171 @@ hash = "sha256-ISyVjtr9vGzCqq7oaDQd6h9kC7iumyo38z9VjuVCsu4="; }; in - stdenvNoCC.mkDerivation rec { - inherit pname version; - - src = ./.; - - nativeBuildInputs = [ - p7zip - nodePackages.asar - makeWrapper - imagemagick - icoutils - perl +stdenvNoCC.mkDerivation rec { + inherit pname version; + + src = ./.; + + nativeBuildInputs = [ + p7zip + nodePackages.asar + makeWrapper + imagemagick + icoutils + perl + ]; + + desktopItem = makeDesktopItem { + name = "claude"; + exec = "claude-desktop %u"; + icon = "claude"; + type = "Application"; + terminal = false; + desktopName = "Claude"; + genericName = "Claude Desktop"; + startupWMClass = "claude"; + categories = [ + "Office" + "Utility" ]; + mimeTypes = [ "x-scheme-handler/claude" ]; + }; - desktopItem = makeDesktopItem { - name = "claude"; - exec = "claude-desktop %u"; - icon = "claude"; - type = "Application"; - terminal = false; - desktopName = "Claude"; - genericName = "Claude Desktop"; - startupWMClass = "claude"; - categories = [ - "Office" - "Utility" - ]; - mimeTypes = ["x-scheme-handler/claude"]; - }; - - buildPhase = '' - runHook preBuild - - # Create temp working directory - mkdir -p $TMPDIR/build - cd $TMPDIR/build - - # Extract installer exe, and nupkg within it - 7z x -y ${srcExe} - 7z x -y "AnthropicClaude-${version}-full.nupkg" - - # Package the icons from claude.exe - wrestool -x -t 14 lib/net45/claude.exe -o claude.ico - icotool -x claude.ico - - for size in 16 24 32 48 64 256; do - mkdir -p $TMPDIR/build/icons/hicolor/"$size"x"$size"/apps - install -Dm 644 claude_*"$size"x"$size"x32.png \ - $TMPDIR/build/icons/hicolor/"$size"x"$size"/apps/claude.png - done - - rm claude.ico - - # Process app.asar files - # We need to replace claude-native-bindings.node in both the - # app.asar package and .unpacked directory - mkdir -p electron-app - cp "lib/net45/resources/app.asar" electron-app/ - cp -r "lib/net45/resources/app.asar.unpacked" electron-app/ - - cd electron-app - asar extract app.asar app.asar.contents - - echo "Using search pattern: '$TARGET_PATTERN' within search base: '$SEARCH_BASE'" - SEARCH_BASE="app.asar.contents/.vite/renderer/main_window/assets" - TARGET_PATTERN="MainWindowPage-*.js" - - echo "Searching for '$TARGET_PATTERN' within '$SEARCH_BASE'..." - # Find the target file recursively (ensure only one matches) - TARGET_FILES=$(find "$SEARCH_BASE" -type f -name "$TARGET_PATTERN") - # Count non-empty lines to get the number of files found - NUM_FILES=$(echo "$TARGET_FILES" | grep -c .) - echo "Found $NUM_FILES matching files" - echo "Target files: $TARGET_FILES" - - echo "##############################################################" - echo "Removing "'!'" from 'if ("'!'"isWindows && isMainWindow) return null;'" - echo "detection flag to to enable title bar" - - echo "Current working directory: '$PWD'" - - echo "Searching for '$TARGET_PATTERN' within '$SEARCH_BASE'..." - # Find the target file recursively (ensure only one matches) - if [ "$NUM_FILES" -eq 0 ]; then - echo "Error: No file matching '$TARGET_PATTERN' found within '$SEARCH_BASE'." >&2 - exit 1 - elif [ "$NUM_FILES" -gt 1 ]; then - echo "Error: Expected exactly one file matching '$TARGET_PATTERN' within '$SEARCH_BASE', but found $NUM_FILES." >&2 - echo "Found files:" >&2 - echo "$TARGET_FILES" >&2 - exit 1 + buildPhase = '' + runHook preBuild + + # Create temp working directory + mkdir -p $TMPDIR/build + cd $TMPDIR/build + + # Extract installer exe, and nupkg within it + 7z x -y ${srcExe} + 7z x -y "AnthropicClaude-${version}-full.nupkg" + + # Package the icons from claude.exe + wrestool -x -t 14 lib/net45/claude.exe -o claude.ico + icotool -x claude.ico + + for size in 16 24 32 48 64 256; do + mkdir -p $TMPDIR/build/icons/hicolor/"$size"x"$size"/apps + install -Dm 644 claude_*"$size"x"$size"x32.png \ + $TMPDIR/build/icons/hicolor/"$size"x"$size"/apps/claude.png + done + + rm claude.ico + + # Process app.asar files + # We need to replace claude-native-bindings.node in both the + # app.asar package and .unpacked directory + mkdir -p electron-app + cp "lib/net45/resources/app.asar" electron-app/ + cp -r "lib/net45/resources/app.asar.unpacked" electron-app/ + + cd electron-app + asar extract app.asar app.asar.contents + + echo "Using search pattern: '$TARGET_PATTERN' within search base: '$SEARCH_BASE'" + SEARCH_BASE="app.asar.contents/.vite/renderer/main_window/assets" + TARGET_PATTERN="MainWindowPage-*.js" + + echo "Searching for '$TARGET_PATTERN' within '$SEARCH_BASE'..." + # Find the target file recursively (ensure only one matches) + TARGET_FILES=$(find "$SEARCH_BASE" -type f -name "$TARGET_PATTERN") + # Count non-empty lines to get the number of files found + NUM_FILES=$(echo "$TARGET_FILES" | grep -c .) + echo "Found $NUM_FILES matching files" + echo "Target files: $TARGET_FILES" + + echo "##############################################################" + echo "Removing "'!'" from 'if ("'!'"isWindows && isMainWindow) return null;'" + echo "detection flag to to enable title bar" + + echo "Current working directory: '$PWD'" + + echo "Searching for '$TARGET_PATTERN' within '$SEARCH_BASE'..." + # Find the target file recursively (ensure only one matches) + if [ "$NUM_FILES" -eq 0 ]; then + echo "Error: No file matching '$TARGET_PATTERN' found within '$SEARCH_BASE'." >&2 + exit 1 + elif [ "$NUM_FILES" -gt 1 ]; then + echo "Error: Expected exactly one file matching '$TARGET_PATTERN' within '$SEARCH_BASE', but found $NUM_FILES." >&2 + echo "Found files:" >&2 + echo "$TARGET_FILES" >&2 + exit 1 + else + # Exactly one file found + TARGET_FILE="$TARGET_FILES" # Assign the found file path + echo "Found target file: $TARGET_FILE" + + echo "Attempting to replace patterns like 'if(!VAR1 && VAR2)' with 'if(VAR1 && VAR2)' in $TARGET_FILE..." + perl -i -pe \ + 's{if\(!(\w+)\s*&&\s*(\w+)\)}{if($1 && $2)}g' \ + "$TARGET_FILE" + + # Verification: Check if the original pattern structure still exists + if ! grep -q -E '!\w+&&\w+' "$TARGET_FILE"; then + echo "Successfully replaced patterns like '!VAR1&&VAR2' with 'VAR1&&VAR2' in $TARGET_FILE" else - # Exactly one file found - TARGET_FILE="$TARGET_FILES" # Assign the found file path - echo "Found target file: $TARGET_FILE" - - echo "Attempting to replace patterns like 'if(!VAR1 && VAR2)' with 'if(VAR1 && VAR2)' in $TARGET_FILE..." - perl -i -pe \ - 's{if\(!(\w+)\s*&&\s*(\w+)\)}{if($1 && $2)}g' \ - "$TARGET_FILE" - - # Verification: Check if the original pattern structure still exists - if ! grep -q -E '!\w+&&\w+' "$TARGET_FILE"; then - echo "Successfully replaced patterns like '!VAR1&&VAR2' with 'VAR1&&VAR2' in $TARGET_FILE" - else - echo "Warning: Some instances of '!VAR1&&VAR2' might still exist in $TARGET_FILE." >&2 - fi # Verification: Check if the original pattern structure still exists - fi - echo "##############################################################" - # exit 1 - - # Replace native bindings - cp ${patchy-cnb}/lib/patchy-cnb.*.node app.asar.contents/node_modules/claude-native/claude-native-binding.node - cp ${patchy-cnb}/lib/patchy-cnb.*.node app.asar.unpacked/node_modules/claude-native/claude-native-binding.node - - # .vite/build/index.js in the app.asar expects the Tray icons to be - # placed inside the app.asar. - mkdir -p app.asar.contents/resources - ls ../lib/net45/resources/ - cp ../lib/net45/resources/Tray* app.asar.contents/resources/ - - # Copy i18n json files - mkdir -p app.asar.contents/resources/i18n - cp ../lib/net45/resources/*.json app.asar.contents/resources/i18n/ - - # Repackage app.asar - asar pack app.asar.contents app.asar - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - # Electron directory structure - mkdir -p $out/lib/$pname - cp -r $TMPDIR/build/electron-app/app.asar $out/lib/$pname/ - cp -r $TMPDIR/build/electron-app/app.asar.unpacked $out/lib/$pname/ - - # Install icons - mkdir -p $out/share/icons - cp -r $TMPDIR/build/icons/* $out/share/icons - - # Install .desktop file - mkdir -p $out/share/applications - install -Dm0644 ${desktopItem}/share/applications/claude.desktop $out/share/applications/claude.desktop - - # Create wrapper - mkdir -p $out/bin - makeWrapper ${electron}/bin/electron $out/bin/$pname \ - --add-flags "$out/lib/$pname/app.asar" \ - --add-flags "--openDevTools" \ - --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations}}" - - runHook postInstall - ''; - - dontUnpack = true; - dontConfigure = true; - - meta = with lib; { - description = "Claude Desktop for Linux"; - license = licenses.unfree; - platforms = platforms.unix; - sourceProvenance = with sourceTypes; [binaryNativeCode]; - mainProgram = pname; - }; - } + echo "Warning: Some instances of '!VAR1&&VAR2' might still exist in $TARGET_FILE." >&2 + fi # Verification: Check if the original pattern structure still exists + fi + echo "##############################################################" + # exit 1 + + # Replace native bindings + cp ${patchy-cnb}/lib/patchy-cnb.*.node app.asar.contents/node_modules/claude-native/claude-native-binding.node + cp ${patchy-cnb}/lib/patchy-cnb.*.node app.asar.unpacked/node_modules/claude-native/claude-native-binding.node + + # .vite/build/index.js in the app.asar expects the Tray icons to be + # placed inside the app.asar. + mkdir -p app.asar.contents/resources + ls ../lib/net45/resources/ + cp ../lib/net45/resources/Tray* app.asar.contents/resources/ + + # Copy i18n json files + mkdir -p app.asar.contents/resources/i18n + cp ../lib/net45/resources/*.json app.asar.contents/resources/i18n/ + + # Repackage app.asar + asar pack app.asar.contents app.asar + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + # Electron directory structure + mkdir -p $out/lib/$pname + cp -r $TMPDIR/build/electron-app/app.asar $out/lib/$pname/ + cp -r $TMPDIR/build/electron-app/app.asar.unpacked $out/lib/$pname/ + + # Install icons + mkdir -p $out/share/icons + cp -r $TMPDIR/build/icons/* $out/share/icons + + # Install .desktop file + mkdir -p $out/share/applications + install -Dm0644 ${desktopItem}/share/applications/claude.desktop $out/share/applications/claude.desktop + + # Create wrapper + mkdir -p $out/bin + makeWrapper ${electron}/bin/electron $out/bin/$pname \ + --add-flags "$out/lib/$pname/app.asar" \ + --add-flags "--openDevTools" \ + --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations}}" + + runHook postInstall + ''; + + dontUnpack = true; + dontConfigure = true; + + meta = with lib; { + description = "Claude Desktop for Linux"; + license = licenses.unfree; + platforms = platforms.unix; + sourceProvenance = with sourceTypes; [ binaryNativeCode ]; + mainProgram = pname; + }; +} diff --git a/pkgs/patchy-cnb.nix b/pkgs/patchy-cnb.nix index b8e96d0..942b78c 100644 --- a/pkgs/patchy-cnb.nix +++ b/pkgs/patchy-cnb.nix @@ -1,52 +1,49 @@ { lib, - stdenv, - cargo, rustPlatform, - rustc, napi-rs-cli, nodejs, - libiconv, -}: let +}: +let patchy-cnb-repo = ../patchy-cnb; in - rustPlatform.buildRustPackage rec { - pname = "patchy-cnb"; - version = "0.1.0"; +rustPlatform.buildRustPackage { + pname = "patchy-cnb"; + version = "0.1.0"; - src = patchy-cnb-repo; + src = patchy-cnb-repo; - cargoLock = { - lockFile = "${patchy-cnb-repo}/Cargo.lock"; - }; + cargoLock = { + lockFile = "${patchy-cnb-repo}/Cargo.lock"; + }; - nativeBuildInputs = [ - napi-rs-cli - nodejs - ]; + nativeBuildInputs = [ + napi-rs-cli + nodejs + ]; - buildPhase = '' - runHook preBuild + buildPhase = '' + runHook preBuild - npm run build --offline + npm run build --offline - runHook postBuild - ''; + runHook postBuild + ''; - installPhase = '' - runHook preInstall + installPhase = '' + runHook preInstall - mkdir -p $out/lib - cp patchy-cnb.*.node $out/lib/ + mkdir -p $out/lib + cp patchy-cnb.*.node $out/lib/ - runHook postInstall - ''; + runHook postInstall + ''; - meta = with lib; { - description = "Stub replacement for claude-native-bindings.node, for use in claude-desktop"; - license = with licenses; [ - mit # or - asl20 - ]; - }; - } + meta = with lib; { + description = "Stub replacement for claude-native-bindings.node, for use in claude-desktop"; + license = with licenses; [ + mit # or + asl20 + ]; + }; +}