From 22cebe132cb08ea259295090f056ab63db612121 Mon Sep 17 00:00:00 2001 From: Davis Davalos-DeLosh Date: Sat, 31 Jan 2026 18:33:21 -0700 Subject: [PATCH] fix: use macOS DMG source with JS stubs, update to v1.1.1200 Major changes: - Switch from Windows installer to macOS DMG (more reliable extraction) - Replace Rust native bindings (patchy-cnb) with simple JavaScript stubs - Update to Claude Desktop v1.1.1200 - Add Linux platform detection for Claude Code - Fix tray icon theme detection for dark/light modes - Simplify build process (no Rust compilation needed) The previous Rust-based approach required compilation and had issues with the tray menu patches injecting 'await' outside async contexts, causing SyntaxError on startup. The JS stub approach matches what claude-desktop-debian uses and is simpler to maintain. --- CLAUDE.md | 41 ++ README.md | 93 ++-- flake.nix | 96 +++-- patchy-cnb/.cargo/config.toml | 2 - patchy-cnb/.gitignore | 196 --------- patchy-cnb/.npmignore | 12 - patchy-cnb/Cargo.lock | 271 ------------ patchy-cnb/Cargo.toml | 19 - patchy-cnb/build.rs | 5 - patchy-cnb/index.d.ts | 360 ---------------- patchy-cnb/npm/linux-x64-gnu/README.md | 3 - patchy-cnb/npm/linux-x64-gnu/package.json | 21 - patchy-cnb/npm/win32-x64-msvc/README.md | 3 - patchy-cnb/npm/win32-x64-msvc/package.json | 18 - patchy-cnb/package.json | 37 -- patchy-cnb/rustfmt.toml | 2 - patchy-cnb/src/lib.rs | 474 --------------------- pkgs/claude-desktop.nix | 423 ++++++++++-------- pkgs/patchy-cnb.nix | 52 --- 19 files changed, 376 insertions(+), 1752 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 patchy-cnb/.cargo/config.toml delete mode 100644 patchy-cnb/.gitignore delete mode 100644 patchy-cnb/.npmignore delete mode 100644 patchy-cnb/Cargo.lock delete mode 100644 patchy-cnb/Cargo.toml delete mode 100644 patchy-cnb/build.rs delete mode 100644 patchy-cnb/index.d.ts delete mode 100644 patchy-cnb/npm/linux-x64-gnu/README.md delete mode 100644 patchy-cnb/npm/linux-x64-gnu/package.json delete mode 100644 patchy-cnb/npm/win32-x64-msvc/README.md delete mode 100644 patchy-cnb/npm/win32-x64-msvc/package.json delete mode 100644 patchy-cnb/package.json delete mode 100644 patchy-cnb/rustfmt.toml delete mode 100644 patchy-cnb/src/lib.rs delete mode 100644 pkgs/patchy-cnb.nix diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..264d208 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,41 @@ +# CLAUDE.md + +## Project Overview + +Nix flake that runs Claude Desktop on Linux by repackaging the macOS build with JavaScript stubs for the native bindings. + +## Building + +```bash +nix build .#claude-desktop # Standard build +nix build .#claude-desktop-with-fhs # With FHS environment for MCP servers +nix run . # Build and run +``` + +## Architecture + +The build process (`pkgs/claude-desktop.nix`): + +1. Download macOS DMG +2. Extract with 7z (handles HFS+ despite warnings) +3. Extract and patch `app.asar`: + - Title bar: Enable native frames on Linux + - Platform detection: Add `linux-x64`/`linux-arm64` for Claude Code + - Origin validation: Allow `file://` protocol when unpackaged + - Tray icons: Theme-aware selection + - Window blur: Fix quick-submit focus +4. Replace `@ant/claude-native` with inline JS stubs +5. Repackage and wrap with Electron + +## Updating Version + +In `pkgs/claude-desktop.nix`: +1. Update `version` +2. Update `srcDmg.url` with new version/hash from filename +3. Update `srcDmg.hash` (run `nix-prefetch-url ` then `nix hash to-sri sha256:`) + +## Packages + +- `claude-desktop` - Standard build +- `claude-desktop-with-fhs` - FHS wrapper for MCP servers (npx, uvx, docker) +- `claude-desktop-shell` - FHS shell for MCP development diff --git a/README.md b/README.md index 91c633e..82ce1ff 100644 --- a/README.md +++ b/README.md @@ -4,104 +4,63 @@ If you run into an issue with this build script, make an issue here. Don't bug A # Claude Desktop for Linux (Nix) -Supports MCP! -![image](https://github.com/user-attachments/assets/93080028-6f71-48bd-8e59-5149d148cd45) +This is a Nix flake for running Claude Desktop on Linux with proper desktop integration. -Supports the Ctrl+Alt+Space popup! -![image](https://github.com/user-attachments/assets/1deb4604-4c06-4e4b-b63f-7f6ef9ef28c1) +## Features -Supports the Tray menu! (Screenshot of running on KDE) +- MCP server support +- Ctrl+Alt+Space popup +- System tray integration +- GNOME/Wayland desktop integration -![image](https://github.com/user-attachments/assets/ba209824-8afb-437c-a944-b53fd9ecd559) +## Usage -This is a Nix flake for running Claude Desktop on Linux. - -# Usage - -To run this once, make sure Nix is installed, then run +To run once: ```bash NIXPKGS_ALLOW_UNFREE=1 nix run github:k3d3/claude-desktop-linux-flake --impure ``` -The "unfree" part is due to the fact that Claude Desktop is not an open source application, and thus, Nix's licensing rules -are dictated by the application itself, not the build script used to build the application. +The "unfree" flag is required because Claude Desktop itself is proprietary. ## Installation on NixOS with Flakes -Add the following to your `flake.nix`: +Add 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`: +Then add to `environment.systemPackages` or `home.packages`: ```nix inputs.claude-desktop.packages.${system}.claude-desktop ``` -If you would like to run [MCP servers with Claude Desktop](https://modelcontextprotocol.io/quickstart/user) on NixOS, use the `claude-desktop-with-fhs` package. This will allow running MCP servers with calls to `npx`, `uvx`, or `docker` (assuming docker is installed). +For [MCP servers](https://modelcontextprotocol.io/quickstart/user) (`npx`, `uvx`, `docker`), use the FHS variant: ```nix inputs.claude-desktop.packages.${system}.claude-desktop-with-fhs ``` -## Other distributions - -This repository only provides a Nix flake, and does not provide a package for e.g. Ubuntu, Fedora, or Arch Linux. - -Other known variants: -- https://github.com/aaddrick/claude-desktop-debian - A debian builder for Claude Desktop -- https://aur.archlinux.org/packages/claude-desktop-bin - An Arch package for Claude Desktop -- https://github.com/wankdanker/claude-desktop-linux-bash - A bash-based Claude Desktop builder that works on Ubuntu and possibly other Debian derivatives - -If anyone else packages Claude Desktop for other distributions, make an issue or PR and I'll link it here. - -# How it works - -Claude Desktop is an Electron application. That means the majority of the application is inside an `app.asar` archive, which usually contains minified Javascript, HTML, and CSS, along with images and a few other things. - -Despite there being no official Linux Claude Desktop release, the vast majority of the code is completely cross-platform. - -With the exception of one library. - -## `claude-native-bindings` - -![image](https://github.com/user-attachments/assets/9b386f42-2565-441a-a351-9c09347f9f5f) - -Node, and by extension Electron, allow you to import natively-compiled objects into the Node runtime as if they were regular modules. -These are typically used to extend the functionality in ways Node itself can't do. Only problem, as shown above, is that these objects -are only compiled for one OS. - -Luckily enough, because it's a loadable Node module, that means you can open it up yourself in node and inspect it - no decompilation or disassembly needed: - -![image](https://github.com/user-attachments/assets/b2f1e72c-f763-45c0-8631-2de5555ae653) - -There are many functions here for getting monitor/window information, as well as for controlling the mouse and keyboard. -I'm not sure what exactly these are for - my best guess is something unreleased related to [Computer Use](https://docs.anthropic.com/en/docs/build-with-claude/computer-use), -however I'm not a huge fan of this functionality existing in the first place. - -As for how to move forward with getting Claude Desktop working on Linux, seeing as how the API surface area of this module is relatively -small, it looked fairly easy to just wholesale reimplement it, using stubs for the functionality. - -## `patchy-cnb` +## Other Distributions -The result of that is a library I call `patchy-cnb`, which uses NAPI-RS to match the original API with stub functions. -Turns out, the original module also used NAPI-RS. Neat! +- [claude-desktop-debian](https://github.com/aaddrick/claude-desktop-debian) - Debian/Ubuntu +- [AUR package](https://aur.archlinux.org/packages/claude-desktop-bin) - Arch Linux +- [claude-desktop-linux-bash](https://github.com/wankdanker/claude-desktop-linux-bash) - Ubuntu/Debian (bash-based) -From there, it's just a matter of compiling `patchy-cnb`, repackaging the app.asar to include the newly built Linux module, and -making a new Electron build with these files. +## How it Works -# License +Claude Desktop is an Electron app. The macOS DMG is extracted, patched for Linux compatibility, and repackaged: -The build scripts in this repository, as well as `patchy-cnb`, are dual-licensed under the terms of the MIT license and the Apache License (Version 2.0). +1. Extract `app.asar` from the macOS build +2. Patch title bar detection, platform checks, and tray icon handling +3. Replace `@ant/claude-native` Windows bindings with JavaScript stubs +4. Repackage with Linux Electron -See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details. +The native binding stubs provide no-op implementations for Windows-specific features (window effects, input emulation, etc.) that aren't needed on Linux. -The Claude Desktop application, not included in this repository, is likely covered by [Anthropic's Consumer Terms](https://www.anthropic.com/legal/consumer-terms). +## License -## Contribution +Build scripts are dual-licensed under MIT and Apache 2.0. See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE). -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any -additional terms or conditions. +Claude Desktop itself is covered by [Anthropic's Consumer Terms](https://www.anthropic.com/legal/consumer-terms). diff --git a/flake.nix b/flake.nix index d1697eb..3cdfbc8 100644 --- a/flake.nix +++ b/flake.nix @@ -6,44 +6,68 @@ 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; + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] ( + system: + let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; }; - claude-desktop-with-fhs = pkgs.buildFHSEnv { - name = "claude-desktop"; - targetPkgs = pkgs: - with pkgs; [ - docker - glibc - openssl - nodejs - uv + in + { + packages = rec { + claude-desktop = pkgs.callPackage ./pkgs/claude-desktop.nix { }; + + claude-desktop-with-fhs = pkgs.symlinkJoin { + name = "claude-desktop-with-fhs"; + paths = [ + claude-desktop + (pkgs.buildFHSEnv { + name = "claude-desktop-bwrap"; + targetPkgs = + pkgs: with pkgs; [ + docker + glibc + openssl + nodejs + uv + glib + gvfs + xdg-utils + ]; + runScript = "${claude-desktop}/bin/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/ + postBuild = '' + rm -f $out/bin/claude-desktop + ln -sf $out/bin/claude-desktop-bwrap $out/bin/claude-desktop + ''; + }; + + claude-desktop-shell = pkgs.buildFHSEnv { + name = "claude-desktop-shell"; + targetPkgs = + pkgs: with pkgs; [ + docker + glibc + openssl + nodejs + uv + glib + gvfs + xdg-utils + ]; + runScript = "bash"; + }; - # Copy icons - mkdir -p $out/share/icons - cp -r ${claude-desktop}/share/icons/* $out/share/icons/ - ''; + default = claude-desktop; }; - default = claude-desktop; - }; - }); + } + ); } diff --git a/patchy-cnb/.cargo/config.toml b/patchy-cnb/.cargo/config.toml deleted file mode 100644 index 0c17df0..0000000 --- a/patchy-cnb/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.x86_64-pc-windows-msvc] -rustflags = ["-C", "target-feature=+crt-static"] \ No newline at end of file diff --git a/patchy-cnb/.gitignore b/patchy-cnb/.gitignore deleted file mode 100644 index 13e0679..0000000 --- a/patchy-cnb/.gitignore +++ /dev/null @@ -1,196 +0,0 @@ -# Created by https://www.toptal.com/developers/gitignore/api/node -# Edit at https://www.toptal.com/developers/gitignore?templates=node - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# End of https://www.toptal.com/developers/gitignore/api/node - -# Created by https://www.toptal.com/developers/gitignore/api/macos -# Edit at https://www.toptal.com/developers/gitignore?templates=macos - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -# End of https://www.toptal.com/developers/gitignore/api/macos - -# Created by https://www.toptal.com/developers/gitignore/api/windows -# Edit at https://www.toptal.com/developers/gitignore?templates=windows - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/windows - -#Added by cargo - -/target - -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -*.node diff --git a/patchy-cnb/.npmignore b/patchy-cnb/.npmignore deleted file mode 100644 index bd4e7b1..0000000 --- a/patchy-cnb/.npmignore +++ /dev/null @@ -1,12 +0,0 @@ -target -.cargo -.github -npm -.eslintrc -.prettierignore -rustfmt.toml -yarn.lock -*.node -.yarn -__test__ -renovate.json diff --git a/patchy-cnb/Cargo.lock b/patchy-cnb/Cargo.lock deleted file mode 100644 index c30eb61..0000000 --- a/patchy-cnb/Cargo.lock +++ /dev/null @@ -1,271 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "ctor" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "libloading" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" -dependencies = [ - "cfg-if", - "windows-targets", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "napi" -version = "2.16.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b" -dependencies = [ - "bitflags", - "ctor", - "napi-derive", - "napi-sys", - "once_cell", -] - -[[package]] -name = "napi-build" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a" - -[[package]] -name = "napi-derive" -version = "2.16.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c" -dependencies = [ - "cfg-if", - "convert_case", - "napi-derive-backend", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "napi-derive-backend" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf" -dependencies = [ - "convert_case", - "once_cell", - "proc-macro2", - "quote", - "regex", - "semver", - "syn", -] - -[[package]] -name = "napi-sys" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" -dependencies = [ - "libloading", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "patchy-cnb" -version = "0.0.0" -dependencies = [ - "napi", - "napi-build", - "napi-derive", -] - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "semver" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" - -[[package]] -name = "syn" -version = "2.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/patchy-cnb/Cargo.toml b/patchy-cnb/Cargo.toml deleted file mode 100644 index dbc9659..0000000 --- a/patchy-cnb/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -edition = "2021" -name = "patchy-cnb" -version = "0.0.0" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix -napi = { version = "2.12.2", default-features = false, features = ["napi4"] } -napi-derive = "2.12.2" - -[build-dependencies] -napi-build = "2.0.1" - -[profile.release] -lto = true -strip = "symbols" diff --git a/patchy-cnb/build.rs b/patchy-cnb/build.rs deleted file mode 100644 index 1f866b6..0000000 --- a/patchy-cnb/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate napi_build; - -fn main() { - napi_build::setup(); -} diff --git a/patchy-cnb/index.d.ts b/patchy-cnb/index.d.ts deleted file mode 100644 index 654ddd7..0000000 --- a/patchy-cnb/index.d.ts +++ /dev/null @@ -1,360 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ - -/* auto-generated by NAPI-RS */ - -export declare function sum(a: number, b: number): number -export const enum KeyboardKey { - Num0 = 0, - Num1 = 1, - Num2 = 2, - Num3 = 3, - Num4 = 4, - Num5 = 5, - Num6 = 6, - Num7 = 7, - Num8 = 8, - Num9 = 9, - A = 10, - B = 11, - C = 12, - D = 13, - E = 14, - F = 15, - G = 16, - H = 17, - I = 18, - J = 19, - K = 20, - L = 21, - M = 22, - N = 23, - O = 24, - P = 25, - Q = 26, - R = 27, - S = 28, - T = 29, - U = 30, - V = 31, - W = 32, - X = 33, - Y = 34, - Z = 35, - AbntC1 = 36, - AbntC2 = 37, - Accept = 38, - Add = 39, - Alt = 40, - Apps = 41, - Attn = 42, - Backspace = 43, - Break = 44, - Begin = 45, - BrightnessDown = 46, - BrightnessUp = 47, - BrowserBack = 48, - BrowserFavorites = 49, - BrowserForward = 50, - BrowserHome = 51, - BrowserRefresh = 52, - BrowserSearch = 53, - BrowserStop = 54, - Cancel = 55, - CapsLock = 56, - Clear = 57, - Command = 58, - ContrastUp = 59, - ContrastDown = 60, - Control = 61, - Convert = 62, - Crsel = 63, - DBEAlphanumeric = 64, - DBECodeinput = 65, - DBEDetermineString = 66, - DBEEnterDLGConversionMode = 67, - DBEEnterIMEConfigMode = 68, - DBEEnterWordRegisterMode = 69, - DBEFlushString = 70, - DBEHiragana = 71, - DBEKatakana = 72, - DBENoCodepoint = 73, - DBENoRoman = 74, - DBERoman = 75, - DBESBCSChar = 76, - DBESChar = 77, - Decimal = 78, - Delete = 79, - Divide = 80, - DownArrow = 81, - Eject = 82, - End = 83, - Ereof = 84, - Escape = 85, - Execute = 86, - Excel = 87, - F1 = 88, - F2 = 89, - F3 = 90, - F4 = 91, - F5 = 92, - F6 = 93, - F7 = 94, - F8 = 95, - F9 = 96, - F10 = 97, - F11 = 98, - F12 = 99, - F13 = 100, - F14 = 101, - F15 = 102, - F16 = 103, - F17 = 104, - F18 = 105, - F19 = 106, - F20 = 107, - F21 = 108, - F22 = 109, - F23 = 110, - F24 = 111, - F25 = 112, - F26 = 113, - F27 = 114, - F28 = 115, - F29 = 116, - F30 = 117, - F31 = 118, - F32 = 119, - F33 = 120, - F34 = 121, - F35 = 122, - Function = 123, - Final = 124, - Find = 125, - GamepadA = 126, - GamepadB = 127, - GamepadDPadDown = 128, - GamepadDPadLeft = 129, - GamepadDPadRight = 130, - GamepadDPadUp = 131, - GamepadLeftShoulder = 132, - GamepadLeftThumbstickButton = 133, - GamepadLeftThumbstickDown = 134, - GamepadLeftThumbstickLeft = 135, - GamepadLeftThumbstickRight = 136, - GamepadLeftThumbstickUp = 137, - GamepadLeftTrigger = 138, - GamepadMenu = 139, - GamepadRightShoulder = 140, - GamepadRightThumbstickButton = 141, - GamepadRightThumbstickDown = 142, - GamepadRightThumbstickLeft = 143, - GamepadRightThumbstickRight = 144, - GamepadRightThumbstickUp = 145, - GamepadRightTrigger = 146, - GamepadView = 147, - GamepadX = 148, - GamepadY = 149, - Hangeul = 150, - Hangul = 151, - Hanja = 152, - Help = 153, - Home = 154, - Ico00 = 155, - IcoClear = 156, - IcoHelp = 157, - IlluminationDown = 158, - IlluminationUp = 159, - IlluminationToggle = 160, - IMEOff = 161, - IMEOn = 162, - Insert = 163, - Junja = 164, - Kana = 165, - Kanji = 166, - LaunchApp1 = 167, - LaunchApp2 = 168, - LaunchMail = 169, - LaunchMediaSelect = 170, - Launchpad = 171, - LaunchPanel = 172, - LButton = 173, - LControl = 174, - LeftArrow = 175, - Linefeed = 176, - LMenu = 177, - LShift = 178, - LWin = 179, - MButton = 180, - MediaFast = 181, - MediaNextTrack = 182, - MediaPlayPause = 183, - MediaPrevTrack = 184, - MediaRewind = 185, - MediaStop = 186, - Meta = 187, - MissionControl = 188, - ModeChange = 189, - Multiply = 190, - NavigationAccept = 191, - NavigationCancel = 192, - NavigationDown = 193, - NavigationLeft = 194, - NavigationMenu = 195, - NavigationRight = 196, - NavigationUp = 197, - NavigationView = 198, - NoName = 199, - NonConvert = 200, - None = 201, - Numlock = 202, - Numpad0 = 203, - Numpad1 = 204, - Numpad2 = 205, - Numpad3 = 206, - Numpad4 = 207, - Numpad5 = 208, - Numpad6 = 209, - Numpad7 = 210, - Numpad8 = 211, - Numpad9 = 212, - OEM1 = 213, - OEM102 = 214, - OEM2 = 215, - OEM3 = 216, - OEM4 = 217, - OEM5 = 218, - OEM6 = 219, - OEM7 = 220, - OEM8 = 221, - OEMAttn = 222, - OEMAuto = 223, - OEMAx = 224, - OEMBacktab = 225, - OEMClear = 226, - OEMComma = 227, - OEMCopy = 228, - OEMCusel = 229, - OEMEnlw = 230, - OEMFinish = 231, - OEMFJJisho = 232, - OEMFJLoya = 233, - OEMFJMasshou = 234, - OEMFJRoya = 235, - OEMFJTouroku = 236, - OEMJump = 237, - OEMMinus = 238, - OEMNECEqual = 239, - OEMPA1 = 240, - OEMPA2 = 241, - OEMPA3 = 242, - OEMPeriod = 243, - OEMPlus = 244, - OEMReset = 245, - OEMWsctrl = 246, - Option = 247, - PA1 = 248, - Packet = 249, - PageDown = 250, - PageUp = 251, - Pause = 252, - Play = 253, - Power = 254, - Print = 255, - Processkey = 256, - RButton = 257, - RCommand = 258, - RControl = 259, - Redo = 260, - Return = 261, - RightArrow = 262, - RMenu = 263, - ROption = 264, - RShift = 265, - RWin = 266, - Scroll = 267, - ScrollLock = 268, - Select = 269, - ScriptSwitch = 270, - Separator = 271, - Shift = 272, - ShiftLock = 273, - Sleep = 274, - Snapshot = 275, - Space = 276, - Subtract = 277, - Super = 278, - SysReq = 279, - Tab = 280, - Undo = 281, - UpArrow = 282, - VidMirror = 283, - VolumeDown = 284, - VolumeMute = 285, - VolumeUp = 286, - MicMute = 287, - Windows = 288, - XButton1 = 289, - XButton2 = 290, - Zoom = 291 -} -export const enum ScrollDirection { - Down = 0, - Up = 1 -} -export const enum MouseButton { - Left = 0, - Middle = 1, - Right = 2 -} -export const enum RequestAccessibilityOptions { - ShowDialog = 0, - OnlyRegisterInSettings = 1 -} -export declare function requestAccessibility(options: number): boolean -export declare function getWindowInfo(): Array -export declare function getActiveWindowHandle(): number -export declare function getMonitorInfo(): MonitorInfo -export declare function focusWindow(handle: number): void -export class MousePosition { - x: number - y: number -} -export class MonitorInfo { - x: number - y: number - width: number - height: number - monitorName: string - isPrimary: boolean -} -export class WindowInfo { - handle: number - processId: number - executablePath: string - title: string - x: number - y: number - width: number - height: number -} -export class InputEmulator { - - constructor() - copy(): void - cut(): void - paste(): void - undo(): void - selectAll(): void - held(): Array - pressChars(text: string): void - pressKey(key: Array): void - static pressThenReleaseKey(key: Array): void - releaseChars(text: string): void - releaseKey(key: number): void - setButtonClick(button: number): void - setButtonToggle(button: number): void - getMousePosition(): MousePosition - typeText(text: string): void - setMouseScroll(direction: number, amount: number): void -} diff --git a/patchy-cnb/npm/linux-x64-gnu/README.md b/patchy-cnb/npm/linux-x64-gnu/README.md deleted file mode 100644 index ecbb38f..0000000 --- a/patchy-cnb/npm/linux-x64-gnu/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `patchy-cnb-linux-x64-gnu` - -This is the **x86_64-unknown-linux-gnu** binary for `patchy-cnb` diff --git a/patchy-cnb/npm/linux-x64-gnu/package.json b/patchy-cnb/npm/linux-x64-gnu/package.json deleted file mode 100644 index 2a75a7d..0000000 --- a/patchy-cnb/npm/linux-x64-gnu/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "patchy-cnb-linux-x64-gnu", - "version": "0.0.0", - "os": [ - "linux" - ], - "cpu": [ - "x64" - ], - "main": "patchy-cnb.linux-x64-gnu.node", - "files": [ - "patchy-cnb.linux-x64-gnu.node" - ], - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "libc": [ - "glibc" - ] -} diff --git a/patchy-cnb/npm/win32-x64-msvc/README.md b/patchy-cnb/npm/win32-x64-msvc/README.md deleted file mode 100644 index faf97ec..0000000 --- a/patchy-cnb/npm/win32-x64-msvc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `patchy-cnb-win32-x64-msvc` - -This is the **x86_64-pc-windows-msvc** binary for `patchy-cnb` diff --git a/patchy-cnb/npm/win32-x64-msvc/package.json b/patchy-cnb/npm/win32-x64-msvc/package.json deleted file mode 100644 index 7ccbc5d..0000000 --- a/patchy-cnb/npm/win32-x64-msvc/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "patchy-cnb-win32-x64-msvc", - "version": "0.0.0", - "os": [ - "win32" - ], - "cpu": [ - "x64" - ], - "main": "patchy-cnb.win32-x64-msvc.node", - "files": [ - "patchy-cnb.win32-x64-msvc.node" - ], - "license": "MIT", - "engines": { - "node": ">= 10" - } -} diff --git a/patchy-cnb/package.json b/patchy-cnb/package.json deleted file mode 100644 index 61e0f3e..0000000 --- a/patchy-cnb/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "patchy-cnb", - "version": "0.0.0", - "main": "index.js", - "types": "index.d.ts", - "napi": { - "name": "patchy-cnb", - "triples": { - "defaults": false, - "additional": [ - "x86_64-pc-windows-msvc", - "x86_64-unknown-linux-gnu" - ] - } - }, - "license": "MIT", - "devDependencies": { - "@napi-rs/cli": "^2.18.4", - "ava": "^6.0.1" - }, - "ava": { - "timeout": "3m" - }, - "engines": { - "node": ">= 10" - }, - "scripts": { - "artifacts": "napi artifacts", - "build": "napi build --platform --release", - "build:debug": "napi build --platform", - "prepublishOnly": "napi prepublish -t npm", - "test": "ava", - "universal": "napi universal", - "version": "napi version" - }, - "packageManager": "yarn@4.5.3" -} diff --git a/patchy-cnb/rustfmt.toml b/patchy-cnb/rustfmt.toml deleted file mode 100644 index cab5731..0000000 --- a/patchy-cnb/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -tab_spaces = 2 -edition = "2021" diff --git a/patchy-cnb/src/lib.rs b/patchy-cnb/src/lib.rs deleted file mode 100644 index f39cd32..0000000 --- a/patchy-cnb/src/lib.rs +++ /dev/null @@ -1,474 +0,0 @@ -#![deny(clippy::all)] - -// Stub out all functions, and print when they're called. -// That's it. Just match the API. - -#[macro_use] -extern crate napi_derive; - -#[napi] -pub enum KeyboardKey { - Num0, - Num1, - Num2, - Num3, - Num4, - Num5, - Num6, - Num7, - Num8, - Num9, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - AbntC1, - AbntC2, - Accept, - Add, - Alt, - Apps, - Attn, - Backspace, - Break, - Begin, - BrightnessDown, - BrightnessUp, - BrowserBack, - BrowserFavorites, - BrowserForward, - BrowserHome, - BrowserRefresh, - BrowserSearch, - BrowserStop, - Cancel, - CapsLock, - Clear, - Command, - ContrastUp, - ContrastDown, - Control, - Convert, - Crsel, - DBEAlphanumeric, - DBECodeinput, - DBEDetermineString, - DBEEnterDLGConversionMode, - DBEEnterIMEConfigMode, - DBEEnterWordRegisterMode, - DBEFlushString, - DBEHiragana, - DBEKatakana, - DBENoCodepoint, - DBENoRoman, - DBERoman, - DBESBCSChar, - DBESChar, - Decimal, - Delete, - Divide, - DownArrow, - Eject, - End, - Ereof, - Escape, - Execute, - Excel, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - F25, - F26, - F27, - F28, - F29, - F30, - F31, - F32, - F33, - F34, - F35, - Function, - Final, - Find, - GamepadA, - GamepadB, - GamepadDPadDown, - GamepadDPadLeft, - GamepadDPadRight, - GamepadDPadUp, - GamepadLeftShoulder, - GamepadLeftThumbstickButton, - GamepadLeftThumbstickDown, - GamepadLeftThumbstickLeft, - GamepadLeftThumbstickRight, - GamepadLeftThumbstickUp, - GamepadLeftTrigger, - GamepadMenu, - GamepadRightShoulder, - GamepadRightThumbstickButton, - GamepadRightThumbstickDown, - GamepadRightThumbstickLeft, - GamepadRightThumbstickRight, - GamepadRightThumbstickUp, - GamepadRightTrigger, - GamepadView, - GamepadX, - GamepadY, - Hangeul, - Hangul, - Hanja, - Help, - Home, - Ico00, - IcoClear, - IcoHelp, - IlluminationDown, - IlluminationUp, - IlluminationToggle, - IMEOff, - IMEOn, - Insert, - Junja, - Kana, - Kanji, - LaunchApp1, - LaunchApp2, - LaunchMail, - LaunchMediaSelect, - Launchpad, - LaunchPanel, - LButton, - LControl, - LeftArrow, - Linefeed, - LMenu, - LShift, - LWin, - MButton, - MediaFast, - MediaNextTrack, - MediaPlayPause, - MediaPrevTrack, - MediaRewind, - MediaStop, - Meta, - MissionControl, - ModeChange, - Multiply, - NavigationAccept, - NavigationCancel, - NavigationDown, - NavigationLeft, - NavigationMenu, - NavigationRight, - NavigationUp, - NavigationView, - NoName, - NonConvert, - None, - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - OEM1, - OEM102, - OEM2, - OEM3, - OEM4, - OEM5, - OEM6, - OEM7, - OEM8, - OEMAttn, - OEMAuto, - OEMAx, - OEMBacktab, - OEMClear, - OEMComma, - OEMCopy, - OEMCusel, - OEMEnlw, - OEMFinish, - OEMFJJisho, - OEMFJLoya, - OEMFJMasshou, - OEMFJRoya, - OEMFJTouroku, - OEMJump, - OEMMinus, - OEMNECEqual, - OEMPA1, - OEMPA2, - OEMPA3, - OEMPeriod, - OEMPlus, - OEMReset, - OEMWsctrl, - Option, - PA1, - Packet, - PageDown, - PageUp, - Pause, - Play, - Power, - Print, - Processkey, - RButton, - RCommand, - RControl, - Redo, - Return, - RightArrow, - RMenu, - ROption, - RShift, - RWin, - Scroll, - ScrollLock, - Select, - ScriptSwitch, - Separator, - Shift, - ShiftLock, - Sleep, - Snapshot, - Space, - Subtract, - Super, - SysReq, - Tab, - Undo, - UpArrow, - VidMirror, - VolumeDown, - VolumeMute, - VolumeUp, - MicMute, - Windows, - XButton1, - XButton2, - Zoom, -} - -#[napi] -pub enum ScrollDirection { - Down = 0, - Up = 1, -} - -#[napi] -pub enum MouseButton { - Left = 0, - Middle = 1, - Right = 2, -} - -#[napi] -pub struct MousePosition { - pub x: u32, - pub y: u32, -} - -#[napi] -pub enum RequestAccessibilityOptions { - ShowDialog, - OnlyRegisterInSettings, -} - -#[napi] -pub struct MonitorInfo { - pub x: u32, - pub y: u32, - pub width: u32, - pub height: u32, - pub monitor_name: String, - pub is_primary: bool, -} - -#[napi] -pub struct WindowInfo { - pub handle: u32, - pub process_id: u32, - pub executable_path: String, - pub title: String, - pub x: u32, - pub y: u32, - pub width: u32, - pub height: u32, -} - -#[napi] -pub fn request_accessibility(options: i32) -> bool { - println!("request_accessibility {options}"); - true -} - -#[napi] -pub fn get_window_info() -> Vec { - println!("get_window_info"); - vec![] -} - -#[napi] -pub fn get_active_window_handle() -> u32 { - println!("get_active_window_handle"); - 0 -} - -#[napi] -pub fn get_monitor_info() -> MonitorInfo { - println!("get_monitor_info"); - MonitorInfo { - x: 0, - y: 0, - width: 1920, - height: 1080, - monitor_name: "\\\\.\\DISPLAY21".to_string(), - is_primary: true, - } -} - -#[napi] -pub fn focus_window(handle: u32) { - println!("focus_window {handle}"); -} - -#[napi(constructor)] -pub struct InputEmulator {} - -#[napi] -impl InputEmulator { - #[napi] - pub fn copy(&self) { - println!("IE copy"); - } - - #[napi] - pub fn cut(&self) { - println!("IE cut"); - } - - #[napi] - pub fn paste(&self) { - println!("IE paste"); - } - - #[napi] - pub fn undo(&self) { - println!("IE undo"); - } - - #[napi] - pub fn select_all(&self) { - println!("IE select all"); - } - - #[napi] - pub fn held(&self) -> Vec { - println!("IE held"); - vec![] - } - - #[napi] - pub fn press_chars(&self, text: String) { - println!("IE press chars '{text}'"); - } - - #[napi] - pub fn press_key(&self, key: Vec) { - println!("IE press key {key:?}"); - } - - #[napi] - pub fn press_then_release_key(key: Vec) { - println!("IE press then release key {key:?}"); - } - - #[napi] - pub fn release_chars(&self, text: String) { - println!("IE release chars '{text}'"); - } - - #[napi] - pub fn release_key(&self, key: u32) { - println!("IE release key {key}"); - } - - #[napi] - pub fn set_button_click(&self, button: i32) { - println!("IE set button click {button}"); - } - - #[napi] - pub fn set_button_toggle(&self, button: i32) { - println!("IE set button toggle {button}"); - } - - #[napi] - pub fn get_mouse_position(&self) -> MousePosition { - println!("IE get mouse position"); - MousePosition { x: 0, y: 0 } - } - - #[napi] - pub fn type_text(&self, text: String) { - println!("IE type text '{text}'"); - } - - #[napi] - pub fn set_mouse_scroll(&self, direction: i32, amount: i32) { - println!("IE set mouse scroll {direction} {amount}"); - } -} diff --git a/pkgs/claude-desktop.nix b/pkgs/claude-desktop.nix index 7037953..c30dd24 100644 --- a/pkgs/claude-desktop.nix +++ b/pkgs/claude-desktop.nix @@ -4,193 +4,268 @@ fetchurl, electron, p7zip, - icoutils, + libicns, nodePackages, imagemagick, makeDesktopItem, makeWrapper, - patchy-cnb, - perl -}: let + glib-networking, + writeText, +}: +let pname = "claude-desktop"; - version = "0.14.10"; - srcExe = fetchurl { - # NOTE: `?v=${version}` doesn't actually request a specific version. It's only being used here as a cache buster. - # In the future, this should more properly query GCP storage to get a specific version. - url = "https://storage.googleapis.com/osprey-downloads-c02f6a0d-347c-492b-a752-3e0651722e97/nest-win-x64/Claude-Setup-x64.exe?v=${version}"; - hash = "sha256-Sn/lvMlfKd7b/utFvCxrkWNDJTug4OOSA4lo9YV8aqk="; + version = "1.1.1200"; + srcDmg = fetchurl { + url = "https://downloads.claude.ai/releases/darwin/universal/${version}/Claude-46e5339828adcd54a87842c010c8c0607f729b52.dmg"; + hash = "sha256-bFhonYSsghEMPaf3q6p2Wb/YyV/bqf0agveLA6N3pPQ="; }; + nativeStub = writeText "claude-native-stub.js" '' + // Stub implementation of @ant/claude-native for Linux + 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); + + class AuthRequest { + static isAvailable() { return false; } + async start(url, scheme, windowHandle) { + throw new Error('AuthRequest not available on Linux'); + } + cancel() {} + } + + module.exports = { + getWindowsVersion: () => "10.0.0", + setWindowEffect: () => {}, + removeWindowEffect: () => {}, + getIsMaximized: () => false, + flashFrame: () => {}, + clearFlashFrame: () => {}, + showNotification: () => {}, + setProgressBar: () => {}, + clearProgressBar: () => {}, + setOverlayIcon: () => {}, + clearOverlayIcon: () => {}, + KeyboardKey, + AuthRequest, + requestAccessibility: () => true, + getWindowInfo: () => [], + getActiveWindowHandle: () => 0, + getMonitorInfo: () => ({ + x: 0, y: 0, width: 1920, height: 1080, + monitorName: "DISPLAY1", isPrimary: true + }), + focusWindow: () => {}, + InputEmulator: class { + copy() {} + cut() {} + paste() {} + undo() {} + selectAll() {} + held() { return []; } + pressChars(text) {} + pressKey(key) {} + pressThenReleaseKey(key) {} + releaseChars(text) {} + releaseKey(key) {} + setButtonClick(button) {} + setButtonToggle(button) {} + getMousePosition() { return { x: 0, y: 0 }; } + typeText(text) {} + setMouseScroll(direction, amount) {} + } + }; + ''; 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 + libicns + ]; + + desktopItem = makeDesktopItem { + name = "Claude"; + exec = "claude-desktop %u"; + icon = "claude"; + type = "Application"; + terminal = false; + desktopName = "Claude"; + genericName = "Claude Desktop"; + comment = "AI Assistant by Anthropic"; + startupWMClass = "Claude"; + startupNotify = true; + categories = [ + "Office" + "Utility" + "Network" + "Chat" ]; + 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 - + buildPhase = '' + runHook preBuild - # Extract installer exe, and nupkg within it - 7z x -y ${srcExe} + mkdir -p $TMPDIR/build + cd $TMPDIR/build - # List the directory, in case the nupkg filename changes - ls -al . + # Extract Mac DMG + echo "Extracting Mac DMG..." + 7z x -y ${srcDmg} || true - 7z x -y "AnthropicClaude-${version}-full.nupkg" + if [ ! -d "Claude/Claude.app" ]; then + echo "ERROR: Failed to extract Claude.app from DMG" + ls -la + exit 1 + fi - # Package the icons from claude.exe - wrestool -x -t 14 lib/net45/claude.exe -o claude.ico - icotool -x claude.ico + APP_CONTENTS="$TMPDIR/build/Claude/Claude.app/Contents" + RESOURCES="$APP_CONTENTS/Resources" - 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 \ + # Extract icons + echo "Extracting icons..." + icns2png -x "$RESOURCES/electron.icns" + for size in 16 32 48 128 256 512; do + mkdir -p $TMPDIR/build/icons/hicolor/"$size"x"$size"/apps + if [ -f "electron_"$size"x"$size"x32.png" ]; then + install -Dm 644 "electron_"$size"x"$size"x32.png" \ + $TMPDIR/build/icons/hicolor/"$size"x"$size"/apps/claude.png + elif [ -f "electron_"$size"x"$size".png" ]; then + install -Dm 644 "electron_"$size"x"$size".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 - 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; - }; - } + done + + # Process app.asar + mkdir -p electron-app + cp "$RESOURCES/app.asar" electron-app/ + cp -r "$RESOURCES/app.asar.unpacked" electron-app/ + + cd electron-app + asar extract app.asar app.asar.contents + + INDEX_FILE="app.asar.contents/.vite/build/index.js" + + # =========================================== + # PATCH 1: Title bar (enable on Linux) + # =========================================== + SEARCH_BASE="app.asar.contents/.vite/renderer/main_window/assets" + TARGET_FILE=$(find "$SEARCH_BASE" -type f -name "MainWindowPage-*.js" 2>/dev/null | head -1) + if [ -n "$TARGET_FILE" ]; then + echo "Patching title bar in: $TARGET_FILE" + sed -i -E 's/if\(!([a-zA-Z]+)[[:space:]]*&&[[:space:]]*([a-zA-Z]+)\)/if(\1 \&\& \2)/g' "$TARGET_FILE" + fi + + # =========================================== + # PATCH 2: Linux platform support for Claude Code + # =========================================== + if [ -f "$INDEX_FILE" ]; then + echo "Patching platform detection..." + sed -i 's/if(process.platform==="win32")return"win32-x64";throw/if(process.platform==="win32")return"win32-x64";if(process.platform==="linux")return process.arch==="arm64"?"linux-arm64":"linux-x64";throw/g' "$INDEX_FILE" + + # =========================================== + # PATCH 3: Origin validation for file:// protocol + # =========================================== + echo "Patching origin validation..." + sed -i -E 's/e\.protocol==="file:"\&\&[a-zA-Z]+\.app\.isPackaged===!0/e.protocol==="file:"/g' "$INDEX_FILE" + + # =========================================== + # PATCH 4: Tray icon theme detection + # =========================================== + echo "Patching tray icon theme..." + sed -i -E 's/:([a-zA-Z])="TrayIconTemplate\.png"/:\1=require("electron").nativeTheme.shouldUseDarkColors?"TrayIconTemplate-Dark.png":"TrayIconTemplate.png"/g' "$INDEX_FILE" + + # =========================================== + # PATCH 5: Window blur before hide (fixes quick submit) + # =========================================== + echo "Patching window blur..." + sed -i 's/e\.hide()/e.blur(),e.hide()/g' "$INDEX_FILE" + fi + + # =========================================== + # Install native stub (JavaScript, not Rust) + # =========================================== + echo "Installing native stub..." + mkdir -p app.asar.contents/node_modules/@ant/claude-native + mkdir -p app.asar.unpacked/node_modules/@ant/claude-native + cp ${nativeStub} app.asar.contents/node_modules/@ant/claude-native/index.js + cp ${nativeStub} app.asar.unpacked/node_modules/@ant/claude-native/index.js + + # Swift addon stub (reuse the same stub) + mkdir -p app.asar.contents/node_modules/@ant/claude-swift + mkdir -p app.asar.unpacked/node_modules/@ant/claude-swift + echo "module.exports = {};" > app.asar.contents/node_modules/@ant/claude-swift/index.js + echo "module.exports = {};" > app.asar.unpacked/node_modules/@ant/claude-swift/index.js + + # Copy tray icons + mkdir -p app.asar.contents/resources + cp "$RESOURCES"/TrayIconTemplate*.png app.asar.contents/resources/ 2>/dev/null || true + cp "$RESOURCES"/Tray*.ico app.asar.contents/resources/ 2>/dev/null || true + + # Fix icon opacity for Linux + echo "Fixing tray icon opacity..." + for icon in app.asar.contents/resources/TrayIconTemplate*.png; do + if [ -f "$icon" ]; then + convert "$icon" -channel A -fx "a>0?1:0" "$icon" 2>/dev/null || true + fi + done + + # Copy i18n + mkdir -p app.asar.contents/resources/i18n + cp "$RESOURCES"/*.json app.asar.contents/resources/i18n/ 2>/dev/null || true + + # Repack + asar pack app.asar.contents app.asar + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + 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/ + + mkdir -p $out/share/icons + cp -r $TMPDIR/build/icons/* $out/share/icons + + mkdir -p $out/share/applications + install -Dm0644 {${desktopItem},$out}/share/applications/Claude.desktop + + mkdir -p $out/bin + makeWrapper ${electron}/bin/electron $out/bin/$pname \ + --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ glib-networking ]}" \ + --add-flags "$out/lib/$pname/app.asar" \ + --add-flags "\''${CLAUDE_USE_WAYLAND:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations,UseOzonePlatform --gtk-version=4}" \ + --set GIO_EXTRA_MODULES "${glib-networking}/lib/gio/modules" \ + --set-default GDK_BACKEND "x11" \ + --set CHROME_DESKTOP "Claude.desktop" \ + --set-default GTK_THEME "\''${GTK_THEME:-Adwaita:dark}" \ + --set-default COLOR_SCHEME_PREFERENCE "\''${COLOR_SCHEME_PREFERENCE:-dark}" \ + --prefix XDG_DATA_DIRS : "$out/share" + + 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 deleted file mode 100644 index b8e96d0..0000000 --- a/pkgs/patchy-cnb.nix +++ /dev/null @@ -1,52 +0,0 @@ -{ - lib, - stdenv, - cargo, - rustPlatform, - rustc, - napi-rs-cli, - nodejs, - libiconv, -}: let - patchy-cnb-repo = ../patchy-cnb; -in - rustPlatform.buildRustPackage rec { - pname = "patchy-cnb"; - version = "0.1.0"; - - src = patchy-cnb-repo; - - cargoLock = { - lockFile = "${patchy-cnb-repo}/Cargo.lock"; - }; - - nativeBuildInputs = [ - napi-rs-cli - nodejs - ]; - - buildPhase = '' - runHook preBuild - - npm run build --offline - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - mkdir -p $out/lib - cp patchy-cnb.*.node $out/lib/ - - runHook postInstall - ''; - - meta = with lib; { - description = "Stub replacement for claude-native-bindings.node, for use in claude-desktop"; - license = with licenses; [ - mit # or - asl20 - ]; - }; - }