packages: convert to self-contained flake-parts modules
Each package file now exports its own perSystem.packages.<name> definition instead of being called from a centralized packages.nix file. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1b7ea7e59b
commit
829fde6a3a
6 changed files with 459 additions and 445 deletions
|
|
@ -1,32 +1,35 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
lib,
|
perSystem =
|
||||||
stdenv,
|
{ pkgs, ... }:
|
||||||
fetchFromGitHub,
|
{
|
||||||
}:
|
packages.base16-schemes = pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
pname = "base16-schemes";
|
||||||
pname = "base16-schemes";
|
version = "0-unstable-2025-06-04";
|
||||||
version = "0-unstable-2025-06-04";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = pkgs.fetchFromGitHub {
|
||||||
owner = "tinted-theming";
|
owner = "tinted-theming";
|
||||||
repo = "schemes";
|
repo = "schemes";
|
||||||
rev = "317a5e10c35825a6c905d912e480dfe8e71c7559";
|
rev = "317a5e10c35825a6c905d912e480dfe8e71c7559";
|
||||||
hash = "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=";
|
hash = "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=";
|
||||||
};
|
};
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
runHook preInstall
|
runHook preInstall
|
||||||
|
|
||||||
mkdir -p $out/share/themes/
|
mkdir -p $out/share/themes/
|
||||||
install base16/*.yaml $out/share/themes/
|
install base16/*.yaml $out/share/themes/
|
||||||
|
|
||||||
runHook postInstall
|
runHook postInstall
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "All the color schemes for use in base16 packages";
|
description = "All the color schemes for use in base16 packages";
|
||||||
homepage = "https://github.com/tinted-theming/schemes";
|
homepage = "https://github.com/tinted-theming/schemes";
|
||||||
maintainers = [ lib.maintainers.DamienCassou ];
|
maintainers = [ pkgs.lib.maintainers.DamienCassou ];
|
||||||
license = lib.licenses.mit;
|
license = pkgs.lib.licenses.mit;
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,221 +1,215 @@
|
||||||
|
{ inputs, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
lib,
|
perSystem =
|
||||||
stdenv,
|
{ system, ... }:
|
||||||
fetchurl,
|
let
|
||||||
makeWrapper,
|
pkgs = import inputs.nixpkgs {
|
||||||
makeDesktopItem,
|
inherit system;
|
||||||
copyDesktopItems,
|
config.allowUnfree = true;
|
||||||
p7zip,
|
};
|
||||||
unzip,
|
|
||||||
electron,
|
|
||||||
nodejs,
|
|
||||||
asar,
|
|
||||||
graphicsmagick,
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
pname = "claude-desktop";
|
||||||
pname = "claude-desktop";
|
version = "1.0.1768";
|
||||||
version = "1.0.1768"; # Updated based on extracted nupkg
|
|
||||||
|
|
||||||
srcs.x86_64-linux = fetchurl {
|
srcs.x86_64-linux = pkgs.fetchurl {
|
||||||
url = "https://downloads.claude.ai/releases/win32/x64/1.0.1768/Claude-67d01376d0e9d08b328455f6db9e63b0d603506a.exe";
|
url = "https://downloads.claude.ai/releases/win32/x64/1.0.1768/Claude-67d01376d0e9d08b328455f6db9e63b0d603506a.exe";
|
||||||
hash = "sha256-x76Qav38ya3ObpWIq3dDowo79LgvVquMfaZeH8M1LUk=;";
|
hash = "sha256-x76Qav38ya3ObpWIq3dDowo79LgvVquMfaZeH8M1LUk=;";
|
||||||
};
|
};
|
||||||
|
|
||||||
src =
|
src =
|
||||||
srcs.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}");
|
srcs.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system: ${pkgs.stdenv.hostPlatform.system}");
|
||||||
|
|
||||||
# Stub implementation for claude-native module
|
claudeNativeStub = ''
|
||||||
claudeNativeStub = ''
|
// Stub implementation of claude-native using KeyboardKey enum values
|
||||||
// Stub implementation of claude-native using KeyboardKey enum values
|
const KeyboardKey = {
|
||||||
const KeyboardKey = {
|
Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40,
|
||||||
Backspace: 43, Tab: 280, Enter: 261, Shift: 272, Control: 61, Alt: 40,
|
CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250,
|
||||||
CapsLock: 56, Escape: 85, Space: 276, PageUp: 251, PageDown: 250,
|
End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262,
|
||||||
End: 83, Home: 154, LeftArrow: 175, UpArrow: 282, RightArrow: 262,
|
DownArrow: 81, Delete: 79, Meta: 187
|
||||||
DownArrow: 81, Delete: 79, Meta: 187
|
};
|
||||||
};
|
Object.freeze(KeyboardKey);
|
||||||
Object.freeze(KeyboardKey);
|
module.exports = {
|
||||||
module.exports = {
|
getWindowsVersion: () => "10.0.0",
|
||||||
getWindowsVersion: () => "10.0.0",
|
setWindowEffect: () => {},
|
||||||
setWindowEffect: () => {},
|
removeWindowEffect: () => {},
|
||||||
removeWindowEffect: () => {},
|
getIsMaximized: () => false,
|
||||||
getIsMaximized: () => false,
|
flashFrame: () => {},
|
||||||
flashFrame: () => {},
|
clearFlashFrame: () => {},
|
||||||
clearFlashFrame: () => {},
|
showNotification: () => {},
|
||||||
showNotification: () => {},
|
setProgressBar: () => {},
|
||||||
setProgressBar: () => {},
|
clearProgressBar: () => {},
|
||||||
clearProgressBar: () => {},
|
setOverlayIcon: () => {},
|
||||||
setOverlayIcon: () => {},
|
clearOverlayIcon: () => {},
|
||||||
clearOverlayIcon: () => {},
|
KeyboardKey
|
||||||
KeyboardKey
|
};
|
||||||
};
|
'';
|
||||||
'';
|
in
|
||||||
|
{
|
||||||
|
packages.claude-desktop = pkgs.stdenv.mkDerivation rec {
|
||||||
|
inherit pname version src;
|
||||||
|
|
||||||
in
|
nativeBuildInputs = with pkgs; [
|
||||||
stdenv.mkDerivation rec {
|
makeWrapper
|
||||||
inherit pname version src;
|
copyDesktopItems
|
||||||
|
p7zip
|
||||||
|
unzip
|
||||||
|
nodejs
|
||||||
|
graphicsmagick
|
||||||
|
];
|
||||||
|
|
||||||
nativeBuildInputs = [
|
buildInputs = [ pkgs.electron ];
|
||||||
makeWrapper
|
|
||||||
copyDesktopItems
|
|
||||||
p7zip
|
|
||||||
unzip
|
|
||||||
nodejs
|
|
||||||
graphicsmagick
|
|
||||||
];
|
|
||||||
|
|
||||||
buildInputs = [
|
desktopItems = [
|
||||||
electron
|
(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 = [
|
unpackPhase = ''
|
||||||
(makeDesktopItem {
|
runHook preUnpack
|
||||||
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 = ''
|
# Extract the Windows installer - use -y to auto-overwrite
|
||||||
runHook preUnpack
|
7z x -y $src -o./extracted
|
||||||
|
|
||||||
# Extract the Windows installer - use -y to auto-overwrite
|
# The installer contains a NuGet package
|
||||||
7z x -y $src -o./extracted
|
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
|
# Extract app.asar to modify it
|
||||||
if [ -f ./extracted/AnthropicClaude-*-full.nupkg ]; then
|
if [ -f ./nupkg/lib/net45/resources/app.asar ]; then
|
||||||
echo "Found NuGet package, extracting..."
|
echo "Extracting app.asar..."
|
||||||
# NuGet packages are just zip files
|
${pkgs.asar}/bin/asar extract ./nupkg/lib/net45/resources/app.asar ./app
|
||||||
unzip -q ./extracted/AnthropicClaude-*-full.nupkg -d ./nupkg
|
|
||||||
|
|
||||||
# Extract app.asar to modify it
|
# Also copy the unpacked resources
|
||||||
if [ -f ./nupkg/lib/net45/resources/app.asar ]; then
|
if [ -d ./nupkg/lib/net45/resources/app.asar.unpacked ]; then
|
||||||
echo "Extracting app.asar..."
|
cp -r ./nupkg/lib/net45/resources/app.asar.unpacked/* ./app/
|
||||||
${asar}/bin/asar extract ./nupkg/lib/net45/resources/app.asar ./app
|
fi
|
||||||
|
|
||||||
# Also copy the unpacked resources
|
# Copy additional resources
|
||||||
if [ -d ./nupkg/lib/net45/resources/app.asar.unpacked ]; then
|
mkdir -p ./app/resources
|
||||||
cp -r ./nupkg/lib/net45/resources/app.asar.unpacked/* ./app/
|
mkdir -p ./app/resources/i18n
|
||||||
fi
|
cp ./nupkg/lib/net45/resources/Tray* ./app/resources/ || true
|
||||||
|
cp ./nupkg/lib/net45/resources/*-*.json ./app/resources/i18n/ || true
|
||||||
# Copy additional resources
|
fi
|
||||||
mkdir -p ./app/resources
|
else
|
||||||
mkdir -p ./app/resources/i18n
|
echo "NuGet package not found"
|
||||||
cp ./nupkg/lib/net45/resources/Tray* ./app/resources/ || true
|
ls -la ./extracted/
|
||||||
cp ./nupkg/lib/net45/resources/*-*.json ./app/resources/i18n/ || true
|
exit 1
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
runHook postInstall
|
runHook postUnpack
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = with lib; {
|
buildPhase = ''
|
||||||
description = "Claude Desktop - AI assistant from Anthropic";
|
runHook preBuild
|
||||||
homepage = "https://claude.ai";
|
|
||||||
license = licenses.unfree;
|
# Replace the Windows-specific claude-native module with a stub
|
||||||
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
|
if [ -d ./app/node_modules/claude-native ]; then
|
||||||
maintainers = with maintainers; [ ];
|
echo "Replacing claude-native module with Linux stub..."
|
||||||
platforms = [ "x86_64-linux" ];
|
rm -rf ./app/node_modules/claude-native/*.node
|
||||||
mainProgram = "claude-desktop";
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,84 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
lib,
|
perSystem =
|
||||||
pkgs ? import <nixpkgs> { },
|
{ 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-config = pkgs.writeText "fastfetch-config.json" (
|
||||||
fastfetch-logo = pkgs.fetchurl {
|
builtins.toJSON {
|
||||||
url = "https://discourse.nixos.org/uploads/default/original/3X/3/6/36954e6d6aa32c8b00f50ca43f142d898c1ff535.png";
|
"$schema" = "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json";
|
||||||
hash = "sha256-aLHz8jSAFocrn+Pb4vRq0wtkYFJpBpZRevd+VoZC/PQ=";
|
modules = [
|
||||||
};
|
"title"
|
||||||
|
"separator"
|
||||||
fastfetch-config = pkgs.writeText "fastfetch-config.json" (
|
{
|
||||||
builtins.toJSON {
|
type = "os";
|
||||||
"$schema" = "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json";
|
keyWidth = 9;
|
||||||
modules = [
|
}
|
||||||
"title"
|
{
|
||||||
"separator"
|
type = "kernel";
|
||||||
{
|
keyWidth = 9;
|
||||||
type = "os";
|
}
|
||||||
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";
|
in
|
||||||
keyWidth = 9;
|
{
|
||||||
}
|
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 "$@" '';
|
||||||
{
|
};
|
||||||
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 "$@" ''
|
|
||||||
|
|
|
||||||
|
|
@ -1,105 +1,109 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
pkgs ? import <nixpkgs> { },
|
perSystem =
|
||||||
}:
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
packages.hm-cli = pkgs.writeShellScriptBin "hm" ''
|
||||||
|
set -e
|
||||||
|
|
||||||
pkgs.writeShellScriptBin "hm" ''
|
HM="${pkgs.lib.getExe pkgs.home-manager}"
|
||||||
set -e
|
FLAKE_PATH="''${HM_PATH:-$HOME/.config/home-manager}"
|
||||||
|
FLAKE_OUTPUT="''${HM_USER:-$(whoami)@$(hostname)}"
|
||||||
|
|
||||||
HM="${pkgs.lib.getExe pkgs.home-manager}"
|
show_usage() {
|
||||||
FLAKE_PATH="''${HM_PATH:-$HOME/.config/home-manager}"
|
cat <<EOF
|
||||||
FLAKE_OUTPUT="''${HM_USER:-$(whoami)@$(hostname)}"
|
Usage: hm <command> [args]
|
||||||
|
|
||||||
show_usage() {
|
Commands:
|
||||||
cat <<EOF
|
apply Switch to a new generation
|
||||||
Usage: hm <command> [args]
|
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:
|
Environment Variables:
|
||||||
apply Switch to a new generation
|
HM_PATH Override default flake path (~/.config/home-manager)
|
||||||
generation list List all generations
|
Currently set to "''${HM_PATH:-<not set>}"
|
||||||
generation delete ID... Delete specified generation(s)
|
HM_USER Override default user output ("$(whoami)@$(hostname)")
|
||||||
generation rollback Rollback to the previous generation
|
Currently set to "''${HM_USER:-<not set>}"
|
||||||
generation switch ID Switch to the specified generation
|
EOF
|
||||||
generation cleanup Delete all but the current generation
|
}
|
||||||
|
|
||||||
Environment Variables:
|
if [[ $# -eq 0 ]]; then
|
||||||
HM_PATH Override default flake path (~/.config/home-manager)
|
|
||||||
Currently set to "''${HM_PATH:-<not set>}"
|
|
||||||
HM_USER Override default user output ("$(whoami)@$(hostname)")
|
|
||||||
Currently set to "''${HM_USER:-<not set>}"
|
|
||||||
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"
|
|
||||||
show_usage
|
show_usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$2" in
|
case "$1" in
|
||||||
list)
|
apply)
|
||||||
"$HM" generations
|
"$HM" switch --flake "$FLAKE_PATH#$FLAKE_OUTPUT" -b bkp
|
||||||
;;
|
;;
|
||||||
delete)
|
generation)
|
||||||
if [[ $# -lt 3 ]]; then
|
if [[ $# -lt 2 ]]; then
|
||||||
echo "Error: delete requires at least one generation ID"
|
echo "Error: generation command requires a subcommand"
|
||||||
|
show_usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift 2
|
|
||||||
"$HM" remove-generations "$@"
|
case "$2" in
|
||||||
;;
|
list)
|
||||||
rollback)
|
"$HM" generations
|
||||||
PREV_GEN=$("$HM" generations | \
|
;;
|
||||||
sed -n 's/^[[:space:]]*id \([0-9]\+\).*/\1/p' | \
|
delete)
|
||||||
head -n 2 | tail -n 1)
|
if [[ $# -lt 3 ]]; then
|
||||||
if [[ -z "$PREV_GEN" ]]; then
|
echo "Error: delete requires at least one generation ID"
|
||||||
echo "Error: could not determine previous generation (possibly only one generation exists)"
|
exit 1
|
||||||
exit 1
|
fi
|
||||||
fi
|
shift 2
|
||||||
"$HM" switch --flake "$FLAKE_PATH" --switch-generation "$PREV_GEN" -b bkp
|
"$HM" remove-generations "$@"
|
||||||
;;
|
;;
|
||||||
switch)
|
rollback)
|
||||||
if [[ $# -ne 3 ]]; then
|
PREV_GEN=$("$HM" generations | \
|
||||||
echo "Error: switch requires exactly one generation ID"
|
sed -n 's/^[[:space:]]*id \([0-9]\+\).*/\1/p' | \
|
||||||
exit 1
|
head -n 2 | tail -n 1)
|
||||||
fi
|
if [[ -z "$PREV_GEN" ]]; then
|
||||||
"$HM" switch --flake "$FLAKE_PATH" --switch-generation "$3" -b bkp
|
echo "Error: could not determine previous generation (possibly only one generation exists)"
|
||||||
;;
|
exit 1
|
||||||
cleanup)
|
fi
|
||||||
CURRENT_GEN=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .* (current)$/\1/p')
|
"$HM" switch --flake "$FLAKE_PATH" --switch-generation "$PREV_GEN" -b bkp
|
||||||
if [[ -z "$CURRENT_GEN" ]]; then
|
;;
|
||||||
echo "Error: could not determine current generation"
|
switch)
|
||||||
exit 1
|
if [[ $# -ne 3 ]]; then
|
||||||
fi
|
echo "Error: switch requires exactly one generation ID"
|
||||||
OLD_GENS=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .*/\1/p' | grep -v "^$CURRENT_GEN$")
|
exit 1
|
||||||
if [[ -z "$OLD_GENS" ]]; then
|
fi
|
||||||
echo "No old generations to delete"
|
"$HM" switch --flake "$FLAKE_PATH" --switch-generation "$3" -b bkp
|
||||||
else
|
;;
|
||||||
echo "Deleting generations: $(echo $OLD_GENS | tr '\n' ' ')"
|
cleanup)
|
||||||
echo "$OLD_GENS" | xargs "$HM" remove-generations
|
CURRENT_GEN=$("$HM" generations | sed -n 's/^.*id \([0-9]\+\) .* (current)$/\1/p')
|
||||||
echo "Cleanup complete. Current generation $CURRENT_GEN preserved."
|
if [[ -z "$CURRENT_GEN" ]]; then
|
||||||
fi
|
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
|
show_usage
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
'';
|
||||||
*)
|
};
|
||||||
echo "Error: unknown command '$1'"
|
}
|
||||||
show_usage
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
''
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,21 @@
|
||||||
{ pkgs }:
|
{ ... }:
|
||||||
|
|
||||||
pkgs.symlinkJoin {
|
{
|
||||||
name = "kwrite";
|
perSystem =
|
||||||
paths = [ pkgs.kdePackages.kate ];
|
{ pkgs, ... }:
|
||||||
postBuild = ''
|
{
|
||||||
rm -rf $out/bin/kate \
|
packages.kwrite = pkgs.symlinkJoin {
|
||||||
$out/bin/.kate-wrapped \
|
name = "kwrite";
|
||||||
$out/share/applications/org.kde.kate.desktop \
|
paths = [ pkgs.kdePackages.kate ];
|
||||||
$out/share/man \
|
postBuild = ''
|
||||||
$out/share/icons/hicolor/*/apps/kate.png \
|
rm -rf $out/bin/kate \
|
||||||
$out/share/icons/hicolor/scalable/apps/kate.svg \
|
$out/bin/.kate-wrapped \
|
||||||
$out/share/appdata/org.kde.kate.appdata.xml
|
$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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,52 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
pkgs ? import <nixpkgs> { },
|
perSystem =
|
||||||
}:
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
packages.toggleaudiosink = pkgs.writeShellScriptBin "toggleaudiosink" ''
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
pkgs.writeShellScriptBin "toggleaudiosink" ''
|
sound_server="pipewire"
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
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
|
# Get the ID of the last sink in the array
|
||||||
sink_count=$(${pkgs.pulseaudio}/bin/pactl list sinks | grep -c "Sink #[[:digit:]]")
|
final_sink=''${sinks[$((sink_count - 1))]}
|
||||||
# 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
|
# Find the index of the active sink
|
||||||
final_sink=''${sinks[$((sink_count - 1))]}
|
for index in "''${!sinks[@]}"; do
|
||||||
|
if [[ "''${sinks[$index]}" == "$active_sink" ]]; then
|
||||||
|
active_sink_index=$index
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# Find the index of the active sink
|
# Default to the first sink in the list
|
||||||
for index in "''${!sinks[@]}"; do
|
next_sink=''${sinks[0]}
|
||||||
if [[ "''${sinks[$index]}" == "$active_sink" ]]; then
|
next_sink_index=0
|
||||||
active_sink_index=$index
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Default to the first sink in the list
|
# If we're not at the end of the list, move up the list
|
||||||
next_sink=''${sinks[0]}
|
if [[ $active_sink -ne $final_sink ]]; then
|
||||||
next_sink_index=0
|
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
|
# Change the default sink
|
||||||
if [[ $active_sink -ne $final_sink ]]; then
|
# Get the name of the next sink
|
||||||
next_sink_index=$((active_sink_index + 1))
|
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)
|
||||||
next_sink=''${sinks[$next_sink_index]}
|
${pkgs.pulseaudio}/bin/pactl set-default-sink "$next_sink_name"
|
||||||
fi
|
|
||||||
|
|
||||||
# Change the default sink
|
# Move all inputs to the new sink
|
||||||
# Get the name of the next sink
|
for app in $(${pkgs.pulseaudio}/bin/pactl list sink-inputs | sed -n -e 's/.*Sink Input #\([[:digit:]]\)/\1/p'); do
|
||||||
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 "move-sink-input $app $next_sink"
|
||||||
${pkgs.pulseaudio}/bin/pactl set-default-sink "$next_sink_name"
|
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
|
|
||||||
''
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue