All checks were successful
Build AppImage / build (push) Successful in 8m22s
Wenn PipeWire nicht läuft (z.B. PulseAudio/xRDP), wird pipewiresrc auf Rank 0 gesetzt damit WebKitGTK auf pulsesrc zurückfällt. getUserMedia bekommt 5s Timeout statt endlos zu hängen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
183 lines
6.4 KiB
Nix
183 lines
6.4 KiB
Nix
# Claude Desktop — Nix-Wrapper-Paket
|
||
#
|
||
# Liefert:
|
||
# $out/bin/claude-desktop — Launcher mit LD_LIBRARY_PATH
|
||
# $out/bin/claude-desktop-install — Installer (kopiert Binary nach ~/.local/share)
|
||
# $out/share/applications/… — Desktop-Entry
|
||
# $out/share/icons/… — Icon
|
||
#
|
||
# Das eigentliche Binary lebt unter ~/.local/share/claude-desktop/bin/claude-desktop
|
||
# (writable), damit der Auto-Updater es ersetzen kann. Nix-Store ist read-only und
|
||
# waere deshalb inkompatibel mit dem Rename-Trick in apply_update().
|
||
#
|
||
# Einbinden in /etc/nixos/configuration.nix:
|
||
#
|
||
# environment.systemPackages = [
|
||
# (import /pfad/zum/claude-desktop/nix/default.nix { inherit pkgs; })
|
||
# ];
|
||
#
|
||
# Oder per home-manager:
|
||
# home.packages = [ (import ./claude-desktop/nix/default.nix { inherit pkgs; }) ];
|
||
|
||
{ pkgs ? import <nixpkgs> {} }:
|
||
|
||
let
|
||
# Alle Laufzeit-Libs, die das Tauri-Binary braucht (parallel zu shell.nix).
|
||
runtimeLibs = with pkgs; [
|
||
webkitgtk_4_1
|
||
libappindicator-gtk3
|
||
librsvg
|
||
gtk3
|
||
glib
|
||
cairo
|
||
pango
|
||
gdk-pixbuf
|
||
libsoup_3
|
||
at-spi2-atk
|
||
openssl
|
||
# GStreamer + PipeWire fuer WebKitGTK Audio (Mikrofon, getUserMedia)
|
||
gst_all_1.gstreamer
|
||
gst_all_1.gst-plugins-base
|
||
gst_all_1.gst-plugins-good
|
||
gst_all_1.gst-plugins-bad
|
||
pipewire
|
||
alsa-lib
|
||
];
|
||
|
||
# GStreamer-Plugins fuer WebKitGTK (Audio/Video, WebRTC, Mikrofon via PipeWire)
|
||
gstPlugins = with pkgs.gst_all_1; [
|
||
gst-plugins-base
|
||
gst-plugins-good
|
||
gst-plugins-bad
|
||
];
|
||
|
||
gstPluginPath = pkgs.lib.concatMapStringsSep ":" (p: "${p}/lib/gstreamer-1.0") gstPlugins
|
||
+ ":${pkgs.pipewire}/lib/gstreamer-1.0";
|
||
|
||
# Laufzeit-Binaries die die App spawnen muss (node fuer claude-bridge.js,
|
||
# npm fuer apply_bundle_update, tar fuer Bundle-Extract)
|
||
runtimeBins = [ pkgs.nodejs_22 pkgs.gnutar pkgs.gzip ];
|
||
in
|
||
pkgs.stdenv.mkDerivation {
|
||
pname = "claude-desktop";
|
||
version = "0.1.0";
|
||
# Keine Quelldateien — Wir packen nur Wrapper + Desktop-Entry
|
||
dontUnpack = true;
|
||
|
||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||
|
||
installPhase = ''
|
||
runHook preInstall
|
||
|
||
mkdir -p $out/bin $out/share/applications $out/share/icons/hicolor/256x256/apps
|
||
|
||
# 1) Launcher: startet das User-Binary mit Nix-LD_LIBRARY_PATH
|
||
cat > $out/bin/claude-desktop <<'LAUNCHER'
|
||
#!${pkgs.bash}/bin/bash
|
||
# Claude Desktop — NixOS-Launcher
|
||
set -e
|
||
|
||
APP_DIR="$HOME/.local/share/claude-desktop"
|
||
BIN="$APP_DIR/bin/claude-desktop"
|
||
|
||
if [ ! -x "$BIN" ]; then
|
||
echo "⚠️ Claude-Desktop-Binary nicht gefunden: $BIN" >&2
|
||
echo "" >&2
|
||
echo "Erst installieren (aus fertigem Build in /tmp/claude-target):" >&2
|
||
echo " claude-desktop-install" >&2
|
||
echo "" >&2
|
||
echo "Oder neu bauen im Repo:" >&2
|
||
echo " CARGO_TARGET_DIR=/tmp/claude-target \\" >&2
|
||
echo " nix-shell shell.nix --run 'npm ci && npm run tauri build'" >&2
|
||
echo " claude-desktop-install" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# Marker fuer update.rs: wir laufen unter Nix-Wrapper
|
||
export CLAUDE_DESKTOP_NIX_WRAPPER=1
|
||
export CLAUDE_DESKTOP_BIN="$BIN"
|
||
|
||
# HiDPI/Fractional-Scaling — GTK3 unter Wayland kann kein natives fractional
|
||
# scaling. Workaround: Xwayland erzwingen (der Compositor skaliert das Fenster)
|
||
# und GDK_DPI_SCALE aus Xft.dpi ableiten, damit Fonts scharf bleiben.
|
||
# Override-bar ueber CLAUDE_DESKTOP_SCALE (z.B. "1.5") oder komplett
|
||
# deaktivierbar mit CLAUDE_DESKTOP_SCALE=off.
|
||
if [ "''${CLAUDE_DESKTOP_SCALE:-}" != "off" ]; then
|
||
SCALE="''${CLAUDE_DESKTOP_SCALE:-}"
|
||
if [ -z "$SCALE" ] && command -v xrdb >/dev/null 2>&1 && [ -n "''${DISPLAY:-}" ]; then
|
||
DPI="$(xrdb -query 2>/dev/null | awk -F':\\s*' '/^Xft.dpi:/ {print $2; exit}')"
|
||
if [ -n "$DPI" ] && [ "$DPI" -gt 96 ] 2>/dev/null; then
|
||
# bash-only float-Division: auf 2 Stellen runden
|
||
SCALE="$(awk -v d="$DPI" 'BEGIN { printf "%.2f", d/96 }')"
|
||
fi
|
||
fi
|
||
if [ -n "$SCALE" ] && [ "$SCALE" != "1.00" ] && [ "$SCALE" != "1.0" ]; then
|
||
export GDK_DPI_SCALE="$SCALE"
|
||
# Unter Wayland: Xwayland nutzen, damit der Compositor-Scale greift
|
||
if [ "''${XDG_SESSION_TYPE:-}" = "wayland" ]; then
|
||
export GDK_BACKEND="''${GDK_BACKEND:-x11}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# GStreamer/PipeWire: Wenn PipeWire NICHT laeuft, pipewiresrc runterranken
|
||
# damit WebKitGTK auf pulsesrc zurueckfaellt (sonst haengt getUserMedia endlos)
|
||
if ! pgrep -x pipewire >/dev/null 2>&1; then
|
||
export GST_PLUGIN_FEATURE_RANK="pipewiresrc:0,pipewiresink:0"
|
||
fi
|
||
|
||
exec "$BIN" "$@"
|
||
LAUNCHER
|
||
chmod +x $out/bin/claude-desktop
|
||
|
||
# LD_LIBRARY_PATH + PATH dauerhaft ans Launcher-Script binden
|
||
# PATH: node/npm/tar muessen fuer die Bridge und apply_bundle_update verfuegbar sein
|
||
# GST_PLUGIN_SYSTEM_PATH_1_0: GStreamer-Plugins fuer WebKitGTK (Mikrofon/PipeWire)
|
||
wrapProgram $out/bin/claude-desktop \
|
||
--prefix LD_LIBRARY_PATH : ${pkgs.lib.makeLibraryPath runtimeLibs} \
|
||
--prefix PATH : ${pkgs.lib.makeBinPath runtimeBins} \
|
||
--prefix GST_PLUGIN_SYSTEM_PATH_1_0 : ${gstPluginPath}
|
||
|
||
# 2) Installer: kopiert ein frisch gebautes Binary an den Ziel-Ort
|
||
cat > $out/bin/claude-desktop-install <<'INSTALLER'
|
||
#!${pkgs.bash}/bin/bash
|
||
set -e
|
||
SRC="''${1:-/tmp/claude-target/release/claude-desktop}"
|
||
DEST_DIR="$HOME/.local/share/claude-desktop/bin"
|
||
DEST="$DEST_DIR/claude-desktop"
|
||
|
||
if [ ! -x "$SRC" ]; then
|
||
echo "❌ Quelle nicht gefunden oder nicht ausfuehrbar: $SRC" >&2
|
||
echo "" >&2
|
||
echo "Erst bauen:" >&2
|
||
echo " cd <repo>; CARGO_TARGET_DIR=/tmp/claude-target \\" >&2
|
||
echo " nix-shell shell.nix --run 'npm ci && npm run tauri build'" >&2
|
||
exit 1
|
||
fi
|
||
|
||
mkdir -p "$DEST_DIR"
|
||
cp "$SRC" "$DEST"
|
||
chmod +x "$DEST"
|
||
|
||
echo "✅ Claude Desktop installiert nach $DEST"
|
||
echo " Starten mit: claude-desktop (oder aus KDE-Menue)"
|
||
INSTALLER
|
||
chmod +x $out/bin/claude-desktop-install
|
||
|
||
# 3) Desktop-Entry
|
||
cp ${./claude-desktop.desktop} $out/share/applications/claude-desktop.desktop
|
||
|
||
# 4) Icon
|
||
cp ${../src-tauri/icons/icon.png} $out/share/icons/hicolor/256x256/apps/claude-desktop.png
|
||
|
||
runHook postInstall
|
||
'';
|
||
|
||
meta = with pkgs.lib; {
|
||
description = "Native Desktop-App fuer Claude Code (Wrapper-Paket, Binary in ~/.local/share)";
|
||
homepage = "https://git.data-it-solution.de/data/claude-desktop";
|
||
license = licenses.mit;
|
||
platforms = platforms.linux;
|
||
mainProgram = "claude-desktop";
|
||
};
|
||
}
|