# DesignCraft — the desktop app (`designcraft`) and the CLI/MCP server (`designcraft-cli`), built from this # repository: # # nix build # → ./result/bin/designcraft, ./result/bin/designcraft-cli # # Kept in step with the release workflow (.github/workflows/release.yml) and packaging/: # * `Cargo.lock` drives the dependencies (`cargoLock`), so there is no vendorHash to bump. The # git sources (storytold/craft-libs) are fetched at the revision the lock file pins; # * the desktop file, hicolor icons, MIME types and AppStream metadata are the same files the # .deb/.rpm ship (packaging/linux/), so `apt` and NixOS users see one identical DesignCraft; # * `CRAFT_FONTS_DIR` embeds the fonts from storytold/craft-fonts (the `craft-fonts` flake input, pinned to # the revision the release workflow uses); without it everything builds and runs, but some # scripts (e.g. Japanese, Chinese) have no glyphs; # * `doCheck` is off: the test suite needs corpora and a GPU the Nix sandbox cannot provide # (`cargo xtask ci` is the gate). `pkgs.designcraft.overrideAttrs { doCheck = true; }` runs it. # # Everything in the product is pure Rust, so this needs no build system beyond cargo plus the # libraries winit/wgpu `dlopen` at run time (see `runtimeLibs`). # # Desktop sessions: the window opens natively on Wayland (GNOME/Mutter, Hyprland and other # wlroots-style compositors) and falls back to X11/XWayland. File dialogs go through # xdg-desktop-portal, so the session needs a portal backend: GNOME ships one # (`xdg-desktop-portal-gnome`); on Hyprland enable # xdg.portal = { enable = true; extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; }; # next to `programs.hyprland.enable = true;`. { lib, stdenv, rustPlatform, pkg-config, makeWrapper, # Windowing/GPU libraries: needed at build time by the crates that link them, at run time by the # ones that open them with dlopen() (see `runtimeLibs`), and by the dev shell (flake.nix). wayland, wayland-scanner, libxkbcommon, libx11, libxcb, libxcursor, libxi, libxrandr, vulkan-loader, libglvnd, # rfd opens libdbus-1 at run time to reach the xdg-desktop-portal file chooser (Open/Save dialogs). dbus, # Validates the desktop file and the AppStream metadata we install (as CI does). desktop-file-utils, appstream, # Adds the driver search path (/run/opengl-driver/lib) to the binaries' RPATH so wgpu finds the # installed Vulkan/GL driver on NixOS. Optional: harmless on nixpkgs without it. autoAddDriverRunpath ? null, # storytold/craft-fonts checkout (flake input). `null` builds without the embedded fonts. craft-fonts ? null, # Release date for the AppStream metadata (YYYY-MM-DD); the flake derives it from its own commit. buildDate ? "1970-01-01", }: let root = ../.; # The version lives in exactly one place: `[workspace.package] version` in the root Cargo.toml. version = (builtins.fromTOML (builtins.readFile (root + "/Cargo.toml"))).workspace.package.version; isLinux = stdenv.hostPlatform.isLinux; # AppStream id: also the desktop file name, and the icon name in share/icons/hicolor. appId = "ai.storyteller.designcraft"; # Libraries the binaries open at run time with dlopen(): nothing links them, so no RPATH points at # them. winit loads libwayland-client, libxkbcommon(-x11) and the X11 libraries; wgpu loads the # Vulkan loader or libEGL (libglvnd); rfd loads libdbus-1 for the file chooser portal. NixOS patches the loader to find the drivers in # /run/opengl-driver. The wrapper below points LD_LIBRARY_PATH here. runtimeLibs = lib.optionals isLinux [ libxkbcommon wayland libx11 libxcb libxcursor libxi libxrandr vulkan-loader libglvnd dbus ]; # The two native binaries. The wasm app (apps/designcraft-web) is built by `cargo xtask`, not here. binaries = [ "designcraft" "designcraft-cli" ]; in rustPlatform.buildRustPackage { pname = "designcraft"; inherit version; src = lib.cleanSourceWith { src = lib.cleanSource root; # The Nix files are not part of the build: editing them must not rebuild any Rust. filter = path: _type: !(builtins.elem (baseNameOf path) [ "flake.nix" "flake.lock" "nix" ]); }; cargoLock = { lockFile = root + "/Cargo.lock"; # The git dependencies are pinned by revision in Cargo.lock. allowBuiltinFetchGit = true; }; # Both binaries, as the .deb/.rpm install them (`packaging/linux/package.sh`). cargoBuildFlags = [ "--locked" "-p" "designcraft" "-p" "designcraft-cli" ]; doCheck = false; strictDeps = true; nativeBuildInputs = [ pkg-config ] ++ lib.optionals isLinux [ makeWrapper wayland-scanner desktop-file-utils appstream ] ++ lib.optionals (isLinux && autoAddDriverRunpath != null) [ autoAddDriverRunpath ]; buildInputs = lib.optionals isLinux [ wayland libxkbcommon libx11 libxcb libxcursor libxi libxrandr ]; env = lib.optionalAttrs (craft-fonts != null) { CRAFT_FONTS_DIR = toString craft-fonts; # A malformed font input must fail the build rather than silently ship without glyphs. CRAFT_FONTS_REQUIRED = "1"; }; postInstall = '' # Desktop integration: byte-for-byte the files the .deb/.rpm/.AppImage install. install -Dm644 packaging/linux/${appId}.desktop $out/share/applications/${appId}.desktop install -Dm644 packaging/linux/${appId}.mime.xml $out/share/mime/packages/${appId}.xml cp -r assets/app-icon/hicolor $out/share/icons/ install -d $out/share/metainfo substitute packaging/linux/${appId}.metainfo.xml.in \ $out/share/metainfo/${appId}.metainfo.xml \ --subst-var-by VERSION ${version} \ --subst-var-by DATE ${buildDate} # Licences for everything embedded in the binaries. doc=$out/share/doc/designcraft for f in README.md LICENSE-MIT LICENSE-APACHE NOTICE ATTRIBUTION.md assets/ATTRIBUTION.md; do if [ -f "$f" ]; then install -Dm644 "$f" "$doc/$(basename "$f")"; fi done for f in assets/fonts/OFL-*.txt; do if [ -f "$f" ]; then install -Dm644 "$f" "$doc/$(basename "$f")"; fi done '' + lib.optionalString (craft-fonts != null) '' for lic in ${toString craft-fonts}/fonts/*/OFL.txt; do install -Dm644 "$lic" "$doc/OFL-$(basename "$(dirname "$lic")").txt" done '' + lib.optionalString isLinux '' desktop-file-validate $out/share/applications/${appId}.desktop XDG_CACHE_HOME=$TMPDIR/.cache appstreamcli validate --no-net \ $out/share/metainfo/${appId}.metainfo.xml # makeWrapper keeps the binaries working when the libs above are only dlopen'd. for bin in ${lib.concatStringsSep " " (map (b: "$out/bin/" + b) binaries)}; do wrapProgram "$bin" --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeLibs}" done ''; passthru = { # Shared with the dev shell so `cargo run` inside `nix develop` finds the same dlopen'd libs. inherit runtimeLibs; }; meta = { description = "Page layout and publishing application"; longDescription = '' DesignCraft is a page layout and publishing application: multi-page documents, styles, masters, typography and print output. It ships with designcraft-cli, a headless command runner and MCP server for AI agents. ''; homepage = "https://getartcraft.com/apps/designcraft"; license = with lib.licenses; [ mit asl20 ]; sourceProvenance = with lib.sourceTypes; [ fromSource ]; platforms = lib.platforms.linux ++ lib.platforms.darwin; # `nix run` and the desktop entry both start the GUI. mainProgram = "designcraft"; }; }