{ description = "Playpen: vertical-tab terminal with web panes and saved layouts, built on libghostty-vt"; inputs = { # Ghostty tracks nixpkgs-unstable to get GTK 4.20 / GNOME 49. We follow # suit so that our zig-gobject bindings (shared with Ghostty) match the # GObject introspection data of the GTK we link against. nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; systems = { url = "github:nix-systems/default"; flake = false; }; # Zig 0.16.0 is not in nixpkgs yet; libghostty-vt requires it. zig = { url = "github:mitchellh/zig-overlay"; inputs = { nixpkgs.follows = "nixpkgs"; flake-compat.follows = "flake-compat"; systems.follows = "systems"; }; }; }; outputs = { self, nixpkgs, zig, systems, ... }: let inherit (nixpkgs) lib legacyPackages; forAllSystems = f: lib.genAttrs (import systems) (system: f legacyPackages.${system} system); in { devShells = forAllSystems (pkgs: system: { default = pkgs.mkShell { name = "playpen"; nativeBuildInputs = [ zig.packages.${system}."0.16.0" pkgs.pkg-config pkgs.gdb # The review pane's UI is a React app built by Vite, and `zig build` # runs that build (see build.zig) so the bundle it embeds can never be # older than the source it came from. Nothing else here needs node. pkgs.nodejs_22 # Used by ./shot.sh to run the app inside a throwaway headless # compositor and screenshot it, so UI can be checked without # touching the developer's real session. pkgs.sway pkgs.grim pkgs.wtype ]; buildInputs = with pkgs; [ # GTK4 + libadwaita, the same native UI stack Ghostty uses on Linux. gtk4 libadwaita # Powers the web panes. The 6.0 ABI is the GTK4 build of WebKitGTK; # the 4.x ABIs are GTK3 and would pull in a second, conflicting GTK. webkitgtk_6_0 # TLS for the web panes. GIO has no TLS backend built in — it loads # one as a module — so without this every https:// page fails with # "TLS support is not available" while http:// still works. glib-networking # The GPU stack WebKit's web process renders through. It has to be # this mesa and not the host's — see the shellHook below. mesa glib gobject-introspection pango cairo harfbuzz gdk-pixbuf graphene # Windowing backends GTK links against. wayland libxkbcommon libx11 # Font stack used by Pango for glyph rasterization. fontconfig freetype # Icon themes so the app doesn't render missing-image icons. adwaita-icon-theme hicolor-icon-theme ]; shellHook = '' # The runtime data GTK needs to find at startup: its own GSettings # schemas and the icon themes our widgets pull symbolic icons from. # # These are exported under their own names, rather than only folded # into XDG_DATA_DIRS, because `mise run install` reads them back out # to bake into the installed launcher. The built binary already has # an RPATH into the Nix store and runs fine outside this shell, but # it still has to be pointed at this data or GTK aborts on a missing # schema and falls back to broken-image icons. export PLAYPEN_GSETTINGS_SCHEMA_DIR="${pkgs.gtk4}/share/gsettings-schemas/${pkgs.gtk4.name}/glib-2.0/schemas" export PLAYPEN_RUNTIME_DATA_DIRS="${pkgs.gtk4}/share/gsettings-schemas/${pkgs.gtk4.name}:${pkgs.adwaita-icon-theme}/share:${pkgs.hicolor-icon-theme}/share" # The GIO module holding the TLS backend. Same story as the schemas: # it is found by path at runtime, so the installer bakes it in too. export PLAYPEN_GIO_MODULE_DIR="${pkgs.glib-networking}/lib/gio/modules" export GSETTINGS_SCHEMA_DIR="$PLAYPEN_GSETTINGS_SCHEMA_DIR" export XDG_DATA_DIRS="$PLAYPEN_RUNTIME_DATA_DIRS:$XDG_DATA_DIRS" export GIO_EXTRA_MODULES="$PLAYPEN_GIO_MODULE_DIR''${GIO_EXTRA_MODULES:+:$GIO_EXTRA_MODULES}" # WebKit's web process renders through mesa, and every piece of that # path has to come from the *same* mesa. Two things go wrong on a # non-NixOS host if we leave it alone, and both kill the web process # at startup with "Could not create default EGL display: # EGL_BAD_PARAMETER", leaving web panes permanently blank: # # - libglvnd falls back to /usr/share/glvnd/egl_vendor.d and loads # the *host's* mesa as the EGL vendor, while WebKit is linked # against Nix's libgbm. A GBM device made by one mesa is rejected # by the other. # - Nix's libgbm looks for its backend in /run/opengl-driver/lib/gbm, # a path that only exists on NixOS. # # Pointing all three at this mesa keeps the stack self-consistent. # It still uses the real GPU through /dev/dri; this is not a # software-rendering fallback. export PLAYPEN_EGL_VENDOR_DIR="${pkgs.mesa}/share/glvnd/egl_vendor.d" export PLAYPEN_DRI_DRIVERS_PATH="${pkgs.mesa}/lib/dri" export PLAYPEN_GBM_BACKENDS_PATH="${pkgs.mesa}/lib/gbm" export __EGL_VENDOR_LIBRARY_DIRS="$PLAYPEN_EGL_VENDOR_DIR" export LIBGL_DRIVERS_PATH="$PLAYPEN_DRI_DRIVERS_PATH" export GBM_BACKENDS_PATH="$PLAYPEN_GBM_BACKENDS_PATH" ''; }; }); formatter = forAllSystems (pkgs: _: pkgs.alejandra); }; }