feat: add Nix flake with dev shell, package, and NixOS/Home Manager modules

Reproducible development environment for NixOS/Nix contributors:
- Dev shell with Node 22, system Electron, Playwright, LD_LIBRARY_PATH
  for X11/Wayland/audio libs, activated automatically via direnv
- buildNpmPackage derivation wrapping system Electron with desktop file
  and hicolor icons
- NixOS module (programs.openscreen.enable) with xdg-desktop-portal
- Home Manager module for per-user installation
- Overlay for composing with other flakes

Tested: nix flake show, nix develop, nix build, nixos-rebuild switch
This commit is contained in:
Enriquefft
2026-04-12 13:33:13 -05:00
parent a6ae0e6d98
commit 64cdc0dd3c
7 changed files with 364 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
use flake
+5
View File
@@ -34,3 +34,8 @@ playwright-report/
# Vitest browser mode screenshots
__screenshots__/
# Nix
result
result-*
.direnv/
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1775710090,
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+122
View File
@@ -0,0 +1,122 @@
{
description = "OpenScreen desktop screen recorder with built-in editor";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs =
{ self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
in
{
# -- Per-system outputs (packages, dev shells) --
packages = forAllSystems (pkgs: {
openscreen = pkgs.callPackage ./nix/package.nix { };
default = self.packages.${pkgs.stdenv.hostPlatform.system}.openscreen;
});
devShells = forAllSystems (
pkgs:
let
electron = pkgs.electron;
# Libraries Electron needs at runtime on Linux
runtimeLibs = with pkgs; [
# X11
libx11
libxcomposite
libxdamage
libxext
libxfixes
libxrandr
libxtst
libxcb
libxshmfence
# Wayland
wayland
# GTK / UI toolkit
gtk3
glib
pango
cairo
gdk-pixbuf
atk
at-spi2-atk
at-spi2-core
# Graphics
mesa
libGL
libdrm
vulkan-loader
# Networking / crypto (NSS for Chromium)
nss
nspr
# Audio
alsa-lib
pipewire
pulseaudio
# System
dbus
cups
expat
libnotify
libsecret
util-linux # libuuid
];
in
{
default = pkgs.mkShell {
packages = with pkgs; [
nodejs_22
electron
# Native module compilation
python3
pkg-config
gcc
# Playwright browser tests
playwright-driver.browsers
];
# Electron's prebuilt binary needs these at runtime
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs;
# Tell the npm `electron` package to use the Nix-provided binary
# instead of downloading its own. vite-plugin-electron respects this.
ELECTRON_OVERRIDE_DIST_PATH = "${electron}/lib/electron";
# Playwright browser path for test:browser / test:e2e
PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}";
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1";
shellHook = ''
echo "OpenScreen dev shell node $(node --version), electron v$(electron --version 2>/dev/null | tr -d 'v')"
'';
};
}
);
# -- System-wide outputs (modules, overlay) --
overlays.default = final: _prev: {
openscreen = self.packages.${final.stdenv.hostPlatform.system}.openscreen;
};
nixosModules.default = import ./nix/module.nix self;
homeManagerModules.default = import ./nix/hm-module.nix self;
};
}
+36
View File
@@ -0,0 +1,36 @@
# Home Manager module for OpenScreen
# Usage in flake-based Home Manager config:
#
# inputs.openscreen.url = "github:siddharthvaddem/openscreen";
#
# { inputs, ... }: {
# imports = [ inputs.openscreen.homeManagerModules.default ];
# programs.openscreen.enable = true;
# }
self:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.openscreen;
in
{
options.programs.openscreen = {
enable = lib.mkEnableOption "OpenScreen screen recorder";
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.openscreen;
defaultText = lib.literalExpression "inputs.openscreen.packages.\${pkgs.stdenv.hostPlatform.system}.openscreen";
description = "The OpenScreen package to use.";
};
};
config = lib.mkIf cfg.enable {
home.packages = [ cfg.package ];
};
}
+42
View File
@@ -0,0 +1,42 @@
# NixOS module for OpenScreen
# Usage in flake-based NixOS config:
#
# inputs.openscreen.url = "github:siddharthvaddem/openscreen";
#
# { inputs, ... }: {
# imports = [ inputs.openscreen.nixosModules.default ];
# programs.openscreen.enable = true;
# }
self:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.openscreen;
in
{
options.programs.openscreen = {
enable = lib.mkEnableOption "OpenScreen screen recorder";
package = lib.mkOption {
type = lib.types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.openscreen;
defaultText = lib.literalExpression "inputs.openscreen.packages.\${pkgs.stdenv.hostPlatform.system}.openscreen";
description = "The OpenScreen package to use.";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
# Screen capture on Wayland requires xdg-desktop-portal.
# We enable the base portal; users should also enable a
# desktop-specific portal (e.g. xdg-desktop-portal-gtk,
# xdg-desktop-portal-hyprland) in their DE config.
xdg.portal.enable = lib.mkDefault true;
};
}
+130
View File
@@ -0,0 +1,130 @@
{
lib,
buildNpmPackage,
nodejs_22,
electron,
makeWrapper,
makeDesktopItem,
copyDesktopItems,
}:
buildNpmPackage {
nodejs = nodejs_22;
pname = "openscreen";
version = "1.3.0";
src =
let
fs = lib.fileset;
maybe = fs.maybeMissing;
in
fs.toSource {
root = ../.;
fileset = fs.difference ../. (
fs.unions [
../nix
../flake.nix
../flake.lock
(maybe ../release)
(maybe ../test-results)
(maybe ../playwright-report)
(maybe ../.github)
(maybe ../.vscode)
(maybe ../.idea)
(maybe ../.kiro)
(maybe ../.envrc)
(maybe ../.direnv)
(fs.fileFilter (file: file.hasExt "md") ../.)
]
);
};
npmDepsHash = "sha256-Pd6J9TuggA9vM4s/LjdoK4MoBEivSzAWc/G2+pFOM2U=";
env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
# electron-builder is not needed — we wrap system electron directly
npmFlags = [ "--ignore-scripts" ];
makeCacheWritable = true;
# vite-plugin-electron compiles electron/ sources into dist-electron/
# tsconfig has noEmit — tsc is type-check only
buildPhase = ''
runHook preBuild
npx vite build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p "$out/lib/openscreen"
# Renderer build output (index.html, JS chunks, copied public/ assets)
cp -r dist "$out/lib/openscreen/"
# Main process + preload (compiled by vite-plugin-electron)
cp -r dist-electron "$out/lib/openscreen/"
# Package manifest (electron reads "main" field to find entry point)
cp package.json "$out/lib/openscreen/"
# Strip devDependencies (electron, vitest, biome, playwright, etc.)
npm prune --omit=dev --no-save
cp -r node_modules "$out/lib/openscreen/"
# Asset resolution: when app.isPackaged is false, the main process resolves
# assets at <appPath>/public/assets/. Mirror the electron-builder
# extraResources layout so wallpapers load correctly.
mkdir -p "$out/lib/openscreen/public/assets"
cp -r public/wallpapers "$out/lib/openscreen/public/assets/wallpapers"
# Wrap system electron with the app directory
mkdir -p "$out/bin"
makeWrapper "${electron}/bin/electron" "$out/bin/openscreen" \
--add-flags "$out/lib/openscreen" \
--set ELECTRON_IS_DEV 0
# Install icons to hicolor theme
for size in 16 24 32 48 64 128 256 512 1024; do
icon="icons/icons/png/''${size}x''${size}.png"
if [ -f "$icon" ]; then
install -Dm644 "$icon" \
"$out/share/icons/hicolor/''${size}x''${size}/apps/openscreen.png"
fi
done
runHook postInstall
'';
nativeBuildInputs = [
makeWrapper
copyDesktopItems
];
desktopItems = [
(makeDesktopItem {
name = "openscreen";
desktopName = "OpenScreen";
genericName = "Screen Recorder";
exec = "openscreen %U";
icon = "openscreen";
comment = "Desktop screen recorder with built-in editor";
categories = [
"AudioVideo"
"Video"
"Recorder"
];
startupWMClass = "Openscreen";
terminal = false;
})
];
meta = {
description = "Desktop screen recorder with built-in editor";
homepage = "https://github.com/siddharthvaddem/openscreen";
license = lib.licenses.mit;
mainProgram = "openscreen";
platforms = lib.platforms.linux;
};
}