diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e9f04121..e6b920d6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -3,6 +3,7 @@ name: Release
on:
push:
tags: ["v*"]
+ workflow_dispatch:
jobs:
publish-macos:
@@ -18,6 +19,7 @@ jobs:
cache: npm
- name: Check versions match tag
+ if: github.event_name != 'workflow_dispatch'
run: |
TAG="${GITHUB_REF_NAME#v}"
for PKG in package.json apps/desktop/package.json apps/cli/package.json apps/web/package.json; do
@@ -54,3 +56,83 @@ jobs:
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: npm run publish --workspace=@diffusionstudio/desktop
+
+ publish-linux:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: npm
+
+ - name: Install packaging tools
+ run: sudo apt-get update && sudo apt-get install -y rpm fakeroot
+
+ - run: npm ci
+
+ - name: Provide client env for web build
+ run: cp apps/web/.env.example apps/web/.env
+
+ - name: Build deb and zip
+ run: npm run make --workspace=@diffusionstudio/desktop
+
+ - name: Build rpm
+ run: npm run make:rpm --workspace=@diffusionstudio/desktop
+
+ - name: Build AppImage
+ run: npm run make:appimage --workspace=@diffusionstudio/desktop
+
+ - name: Upload Linux assets
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 \
+ || gh release create "$GITHUB_REF_NAME" --draft --title "$GITHUB_REF_NAME" --generate-notes
+ gh release upload "$GITHUB_REF_NAME" \
+ apps/desktop/out/make/deb/x64/*.deb \
+ apps/desktop/out/make/rpm/x64/*.rpm \
+ apps/desktop/out/make/zip/linux/x64/*.zip \
+ apps/desktop/out/make/*.AppImage \
+ --clobber
+
+ publish-arch:
+ runs-on: ubuntu-latest
+ container: archlinux:latest
+ permissions:
+ contents: write
+ steps:
+ # Install git before checkout; the archlinux image is minimal and
+ # actions/checkout needs it.
+ - name: Install build tools
+ run: |
+ pacman -Sy --noconfirm archlinux-keyring
+ pacman -Syu --noconfirm base-devel git gh curl
+
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - run: npm ci
+
+ - name: Provide client env for web build
+ run: cp apps/web/.env.example apps/web/.env
+
+ - name: Package app (no makers)
+ run: npm run package --workspace=@diffusionstudio/desktop
+
+ - name: Build Arch package
+ run: npm run make:arch --workspace=@diffusionstudio/desktop
+
+ - name: Upload Arch package
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 \
+ || gh release create "$GITHUB_REF_NAME" --draft --title "$GITHUB_REF_NAME" --generate-notes
+ gh release upload "$GITHUB_REF_NAME" apps/desktop/out/arch/*.pkg.tar.zst --clobber
diff --git a/README.md b/README.md
index 5158a6c8..c4b15966 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@
+
@@ -53,6 +54,32 @@ npx skills add diffusionstudio/skills
`/editor` is the main skill you'll use. Ask for what you want in plain language. Behind it is `dapi`, the CLI that drives the app.
+## Install
+
+Download the latest release from the [releases page](https://github.com/diffusionstudio/editor/releases/latest). Linux packages are published alongside the macOS build:
+
+**Linux**
+
+```sh
+# Arch Linux (and derivatives)
+sudo pacman -U diffusion-studio--x86_64.pkg.tar.zst
+
+# Debian / Ubuntu
+sudo dpkg -i diffusion-studio__amd64.deb
+
+# Fedora / RHEL
+sudo dnf install diffusion-studio--1.x86_64.rpm
+```
+
+Or run the portable [AppImage](https://appimage.org) with no installation:
+
+```sh
+chmod +x Diffusion-Studio--x86_64.AppImage
+./Diffusion-Studio--x86_64.AppImage
+```
+
+The packages put both the editor (`diffusion-studio`) and the agent CLI (`dapi`) on your `PATH`.
+
## Prompt examples
@@ -235,6 +262,19 @@ npm run symlink:create --workspace=@diffusionstudio/cli
The link points at the CLI build, which `npm run dev:desktop` refreshes on every start, so the linked `dapi` always runs the latest code.
+### Building Linux packages
+
+```sh
+npm run make:linux --workspace=@diffusionstudio/desktop # deb, rpm, zip, AppImage
+npm run make:arch --workspace=@diffusionstudio/desktop # Arch (.pkg.tar.zst)
+```
+
+Builds require Node 20 (newer Node versions can break Electron Packager's zip
+extraction) plus system tools for each format: `dpkg` and `fakeroot` (deb),
+`rpmbuild` (rpm), and `makepkg` (Arch). AppImage builds fetch `appimagetool`
+automatically. Artifacts land in `apps/desktop/out/make/` and
+`apps/desktop/out/arch/`.
+
Before sending a PR:
```sh
diff --git a/apps/desktop/forge.config.ts b/apps/desktop/forge.config.ts
index e4dbe0e6..e0913cd1 100644
--- a/apps/desktop/forge.config.ts
+++ b/apps/desktop/forge.config.ts
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import type { ForgeConfig } from '@electron-forge/shared-types';
+import { MakerDeb } from '@electron-forge/maker-deb';
import { MakerDMG } from '@electron-forge/maker-dmg';
import { MakerZIP } from '@electron-forge/maker-zip';
import { PublisherGithub } from '@electron-forge/publisher-github';
@@ -41,7 +42,24 @@ const config: ForgeConfig = {
: undefined,
},
makers: [
- new MakerZIP({}, ['darwin']),
+ new MakerZIP({}, ['darwin', 'linux']),
+ new MakerDeb(
+ {
+ options: {
+ name: 'diffusion-studio',
+ productName: 'Diffusion Studio',
+ genericName: 'Video Editor',
+ bin: 'Diffusion Studio',
+ description: 'The professional video editor built for agents',
+ maintainer: 'Diffusion Studio ',
+ homepage: 'https://diffusion.studio',
+ section: 'video',
+ categories: ['AudioVideo', 'Video'],
+ icon: './assets/icon.png',
+ },
+ },
+ ['linux'],
+ ),
new MakerDMG({
name: `Diffusion-Studio-${process.arch}`,
icon: './assets/icon.icns',
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 3fdf7711..732053f7 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -20,11 +20,16 @@
"dev": "npm run build && electron-forge start",
"package": "npm run build && npm run build:web && npm run stage:cli && npm run stage:docs && electron-forge package",
"make": "npm run build && npm run build:web && npm run stage:cli && npm run stage:docs && electron-forge make",
+ "make:linux": "npm run make && npm run make:rpm && npm run make:appimage",
+ "make:rpm": "node scripts/make-rpm.mjs",
+ "make:appimage": "node scripts/make-appimage.mjs",
+ "make:arch": "node scripts/make-arch.mjs",
"publish": "npm run build && npm run build:web && npm run stage:cli && npm run stage:docs && electron-forge publish",
"make:icns": "sh scripts/make-icns.sh"
},
"devDependencies": {
"@electron-forge/cli": "^7.11.1",
+ "@electron-forge/maker-deb": "^7.11.2",
"@electron-forge/maker-dmg": "^7.11.1",
"@electron-forge/maker-zip": "^7.11.1",
"@electron-forge/publisher-github": "^7.11.1",
diff --git a/apps/desktop/scripts/make-appimage.mjs b/apps/desktop/scripts/make-appimage.mjs
new file mode 100644
index 00000000..1cad4323
--- /dev/null
+++ b/apps/desktop/scripts/make-appimage.mjs
@@ -0,0 +1,88 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Builds a Linux AppImage from the `electron-forge package` output. Run it
+// after `electron-forge make` (or `package`), which leaves the unpacked app at
+// `out/Diffusion Studio-linux-x64`.
+//
+// The app binary is named "Diffusion Studio" (with a space); AppImage's
+// desktop `Exec=` and AppRun want a space-free command, so we alias it to
+// `diffusion-studio` via a symlink inside the AppDir.
+//
+// appimagetool is downloaded on first use to ~/.cache/appimagetool (override
+// with APPIMAGETOOL_URL). It runs extracted rather than via FUSE so it works
+// in containers and CI.
+
+import { execFileSync, spawnSync } from "node:child_process";
+import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
+import { createRequire } from "node:module";
+import { homedir } from "node:os";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+
+const desktopDir = join(dirname(fileURLToPath(import.meta.url)), "..");
+const require = createRequire(import.meta.url);
+const pkg = require(join(desktopDir, "package.json"));
+
+const ARCH_LABEL = { x64: "x86_64", arm64: "aarch64" }[process.arch] ?? process.arch;
+const APP_NAME = "Diffusion Studio";
+const BIN_NAME = "diffusion-studio";
+const version = pkg.version;
+
+// Locate the packaged directory electron-forge leaves behind.
+const outDir = join(desktopDir, "out");
+const packaged = readdirSync(outDir).find((entry) => entry.startsWith(`${APP_NAME}-linux-`));
+if (!packaged) {
+ console.error("make-appimage: no packaged app found in out/. Run `electron-forge package` first.");
+ process.exit(1);
+}
+const packagedDir = join(outDir, packaged);
+
+const appDir = join(outDir, "appimage", "AppDir");
+rmSync(join(outDir, "appimage"), { recursive: true, force: true });
+mkdirSync(appDir, { recursive: true });
+
+// The packaged app, flat at the AppDir root (resources/, the binary, etc.).
+cpSync(packagedDir, appDir, { recursive: true });
+
+// Space-free alias for the electron binary, referenced by AppRun + desktop Exec.
+symlinkSync(`./${APP_NAME}`, join(appDir, BIN_NAME));
+
+writeFileSync(
+ join(appDir, "AppRun"),
+ `#!/bin/sh\nSELF=$(readlink -f "$0")\nHERE=$(dirname "$SELF")\nexec "$HERE/${BIN_NAME}" "$@"\n`,
+);
+chmodSync(join(appDir, "AppRun"), 0o755);
+
+writeFileSync(
+ join(appDir, `${BIN_NAME}.desktop`),
+ `[Desktop Entry]\nName=Diffusion Studio\nComment=The professional video editor built for agents\nExec=${BIN_NAME}\nIcon=${BIN_NAME}\nTerminal=false\nType=Application\nCategories=AudioVideo;Video;Graphics;\nStartupWMClass=Diffusion Studio\n`,
+);
+
+copyFileSync(join(desktopDir, "assets", "icon.png"), join(appDir, `${BIN_NAME}.png`));
+
+// Fetch appimagetool (a self-contained AppImage) once, then run it extracted
+// so no FUSE mount is required.
+const toolDir = join(homedir(), ".cache", "appimagetool");
+mkdirSync(toolDir, { recursive: true });
+const tool = join(toolDir, "appimagetool-x86_64.AppImage");
+if (!existsSync(tool)) {
+ const url = process.env.APPIMAGETOOL_URL
+ ?? "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage";
+ console.log(`make-appimage: downloading appimagetool from ${url}`);
+ spawnSync("curl", ["-fsSL", "-o", tool, url], { stdio: "inherit" });
+ chmodSync(tool, 0o755);
+}
+
+const makeDir = join(outDir, "make");
+mkdirSync(makeDir, { recursive: true });
+const output = join(makeDir, `Diffusion-Studio-${version}-${ARCH_LABEL}.AppImage`);
+
+console.log(`make-appimage: building ${output}`);
+execFileSync(tool, [appDir, output], {
+ stdio: "inherit",
+ env: { ...process.env, APPIMAGE_EXTRACT_AND_RUN: "1", ARCH: ARCH_LABEL },
+});
+
+console.log(`make-appimage: wrote ${output}`);
diff --git a/apps/desktop/scripts/make-arch.mjs b/apps/desktop/scripts/make-arch.mjs
new file mode 100644
index 00000000..99b4d207
--- /dev/null
+++ b/apps/desktop/scripts/make-arch.mjs
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Stages an Arch Linux package from the `electron-forge package` output and
+// builds it with makepkg. Run after `electron-forge make` (or `package`),
+// which leaves the unpacked app at `out/Diffusion Studio-linux-x64`.
+//
+// The staged source tree lives at out/arch/ and the finished package is
+// out/arch/diffusion-studio--.pkg.tar.zst.
+
+import { execFileSync } from "node:child_process";
+import { cpSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
+import { createRequire } from "node:module";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+
+const desktopDir = join(dirname(fileURLToPath(import.meta.url)), "..");
+const repoRoot = join(desktopDir, "..", "..");
+const require = createRequire(import.meta.url);
+const version = require(join(desktopDir, "package.json")).version;
+
+const APP_NAME = "Diffusion Studio";
+const PKG_NAME = "diffusion-studio";
+
+const outDir = join(desktopDir, "out");
+const packaged = readdirSync(outDir).find((entry) => entry.startsWith(`${APP_NAME}-linux-`));
+if (!packaged) {
+ console.error("make-arch: no packaged app found in out/. Run `electron-forge package` first.");
+ process.exit(1);
+}
+
+const archDir = join(outDir, "arch");
+rmSync(archDir, { recursive: true, force: true });
+mkdirSync(archDir, { recursive: true });
+
+// Source tree that makepkg extracts from the tarball.
+const src = join(archDir, `${PKG_NAME}-${version}-linux-x64`);
+mkdirSync(src, { recursive: true });
+cpSync(join(outDir, packaged), src, { recursive: true });
+
+const packagingDir = join(repoRoot, "packaging", "arch");
+cpSync(join(packagingDir, "launcher"), join(src, "launcher"));
+cpSync(join(packagingDir, `${PKG_NAME}.desktop`), join(src, `${PKG_NAME}.desktop`));
+cpSync(join(desktopDir, "assets", "icon.png"), join(src, `${PKG_NAME}.png`));
+
+// Tar the source (makepkg expects the name in source=()).
+const tarball = `${PKG_NAME}-${version}-linux-x64.tar.gz`;
+execFileSync("tar", ["-czf", join(archDir, tarball), "-C", archDir, `${PKG_NAME}-${version}-linux-x64`], {
+ stdio: "inherit",
+});
+
+// Stage the PKGBUILD with the version substituted.
+const pkbuild = readFileSync(join(packagingDir, "PKGBUILD"), "utf8").replaceAll("__VERSION__", version);
+writeFileSync(join(archDir, "PKGBUILD"), pkbuild);
+
+// Build the package in-place. Requires makepkg (pacman) on the host.
+console.log(`make-arch: building with makepkg in ${archDir}`);
+execFileSync("makepkg", ["-f", "--noconfirm"], {
+ cwd: archDir,
+ stdio: "inherit",
+ env: { ...process.env, PACKAGER: "Diffusion Studio " },
+});
+
+const built = readdirSync(archDir).find((entry) => entry.endsWith(".pkg.tar.zst"));
+console.log(`make-arch: wrote ${join(archDir, built)}`);
diff --git a/apps/desktop/scripts/make-rpm.mjs b/apps/desktop/scripts/make-rpm.mjs
new file mode 100644
index 00000000..7e531804
--- /dev/null
+++ b/apps/desktop/scripts/make-rpm.mjs
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Builds a Fedora RPM from the `electron-forge package` output. Run after
+// `electron-forge make` (or `package`), which leaves the unpacked app at
+// `out/Diffusion Studio-linux-x64`.
+//
+// We generate the .spec ourselves rather than use `@electron-forge/maker-rpm`
+// (electron-installer-redhat): that library stages to `BUILD/usr` and runs
+// `cp -r usr/*` from the build subdir, which breaks on RPM 6's build-directory
+// layout. This script works on both RPM 4 (Fedora) and RPM 6.
+
+import { execFileSync } from "node:child_process";
+import { copyFileSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
+import { createRequire } from "node:module";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+
+const desktopDir = join(dirname(fileURLToPath(import.meta.url)), "..");
+const require = createRequire(import.meta.url);
+const version = require(join(desktopDir, "package.json")).version;
+
+const APP_NAME = "Diffusion Studio";
+const PKG_NAME = "diffusion-studio";
+const RPM_ARCH = { x64: "x86_64", arm64: "aarch64" }[process.arch] ?? process.arch;
+
+const outDir = join(desktopDir, "out");
+const packaged = readdirSync(outDir).find((entry) => entry.startsWith(`${APP_NAME}-linux-`));
+if (!packaged) {
+ console.error("make-rpm: no packaged app found in out/. Run `electron-forge package` first.");
+ process.exit(1);
+}
+const packagedDir = join(outDir, packaged);
+
+const stageDir = join(outDir, "rpmbuild");
+rmSync(stageDir, { recursive: true, force: true });
+mkdirSync(join(stageDir, "SPECS"), { recursive: true });
+
+const desktopFile = join(desktopDir, "..", "..", "packaging", "arch", `${PKG_NAME}.desktop`);
+const iconFile = join(desktopDir, "assets", "icon.png");
+
+const spec = `Name: ${PKG_NAME}
+Version: ${version}
+Release: 1
+Summary: The professional video editor built for agents
+License: MPL-2.0
+URL: https://diffusion.studio
+AutoReqProv: no
+Requires: gtk3, nss, libXScrnSaver, alsa-lib, libXcomposite, libXdamage, libXrandr, libXtst, libXcursor, libXfixes, mesa-libgbm, at-spi2-core, libX11
+
+%description
+The professional video editor built for agents.
+
+%global __strip /bin/true
+%global debug_package %{nil}
+%global __os_install_post %{nil}
+
+%install
+mkdir -p %{buildroot}/usr/lib/${PKG_NAME}
+cp -a '${packagedDir}/.' %{buildroot}/usr/lib/${PKG_NAME}/
+chmod 4755 %{buildroot}/usr/lib/${PKG_NAME}/chrome-sandbox
+mkdir -p %{buildroot}/usr/bin
+ln -s '../lib/${PKG_NAME}/${APP_NAME}' %{buildroot}/usr/bin/${PKG_NAME}
+mkdir -p %{buildroot}/usr/share/applications
+install -m 644 '${desktopFile}' %{buildroot}/usr/share/applications/${PKG_NAME}.desktop
+mkdir -p %{buildroot}/usr/share/pixmaps
+install -m 644 '${iconFile}' %{buildroot}/usr/share/pixmaps/${PKG_NAME}.png
+
+%files
+/usr/bin/${PKG_NAME}
+/usr/lib/${PKG_NAME}/
+/usr/share/applications/${PKG_NAME}.desktop
+/usr/share/pixmaps/${PKG_NAME}.png
+`;
+writeFileSync(join(stageDir, "SPECS", `${PKG_NAME}.spec`), spec);
+
+console.log(`make-rpm: building with rpmbuild (${stageDir})`);
+execFileSync("rpmbuild", ["-bb", join(stageDir, "SPECS", `${PKG_NAME}.spec`), "--define", `_topdir ${stageDir}`], {
+ stdio: "inherit",
+});
+
+// rpmbuild writes to RPMS//; move it next to the other Linux artifacts.
+const rpmsDir = join(stageDir, "RPMS", RPM_ARCH);
+const built = readdirSync(rpmsDir).find((entry) => entry.endsWith(".rpm"));
+const makeDir = join(outDir, "make", "rpm", "x64");
+mkdirSync(makeDir, { recursive: true });
+const output = join(makeDir, built);
+copyFileSync(join(rpmsDir, built), output);
+
+console.log(`make-rpm: wrote ${output}`);
diff --git a/apps/desktop/scripts/stage-cli.mjs b/apps/desktop/scripts/stage-cli.mjs
index 90590cda..70aabc39 100644
--- a/apps/desktop/scripts/stage-cli.mjs
+++ b/apps/desktop/scripts/stage-cli.mjs
@@ -51,6 +51,14 @@ execFileSync("npm", ["install", "--omit=dev", "--no-audit", "--no-fund", "--no-p
// The wrapper runs the CLI bundle on the app's own Electron binary in Node
// mode, so users need no separate Node install. It resolves symlinks first
// because both Homebrew and the in-app installer link it into PATH.
+//
+// The path from the staged `cli/bin` directory to the app binary differs per
+// platform: on macOS the binary lives inside the .app bundle at
+// Contents/MacOS/Diffusion Studio, while on Linux it sits at the packaged
+// directory root (e.g. `Diffusion Studio-linux-x64/Diffusion Studio`).
+const isDarwin = process.platform === "darwin";
+const appPathRel = isDarwin ? "$DIR/../../../.." : "$DIR/../../..";
+const binaryRel = isDarwin ? "$DIR/../../../MacOS/Diffusion Studio" : "$DIR/../../../Diffusion Studio";
const wrapper = `#!/bin/sh
SELF="$0"
while [ -L "$SELF" ]; do
@@ -61,8 +69,8 @@ while [ -L "$SELF" ]; do
esac
done
DIR="$(cd "$(dirname "$SELF")" && pwd)"
-export DIFFUSION_APP_PATH="$(cd "$DIR/../../../.." && pwd)"
-ELECTRON_RUN_AS_NODE=1 exec "$DIR/../../../MacOS/Diffusion Studio" "$DIR/../dapi.js" "$@"
+export DIFFUSION_APP_PATH="$(cd "${appPathRel}" && pwd)"
+ELECTRON_RUN_AS_NODE=1 exec "${binaryRel}" "$DIR/../dapi.js" "$@"
`;
writeFileSync(join(stageDir, "bin", "dapi"), wrapper);
chmodSync(join(stageDir, "bin", "dapi"), 0o755);
diff --git a/package-lock.json b/package-lock.json
index 162608cb..e475cd5c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -59,6 +59,7 @@
},
"devDependencies": {
"@electron-forge/cli": "^7.11.1",
+ "@electron-forge/maker-deb": "^7.11.2",
"@electron-forge/maker-dmg": "^7.11.1",
"@electron-forge/maker-zip": "^7.11.1",
"@electron-forge/publisher-github": "^7.11.1",
@@ -786,6 +787,23 @@
"node": ">= 16.4.0"
}
},
+ "node_modules/@electron-forge/maker-deb": {
+ "version": "7.11.2",
+ "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.11.2.tgz",
+ "integrity": "sha512-MYSdCTsqzKNmsmaq7CIFh2kJdBWUZ4njxnVGrIRClzueVITk5Kots3+eQo+e5QQLvXTVn2XTNDc2nYjvtBh+Mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@electron-forge/maker-base": "7.11.2",
+ "@electron-forge/shared-types": "7.11.2"
+ },
+ "engines": {
+ "node": ">= 16.4.0"
+ },
+ "optionalDependencies": {
+ "electron-installer-debian": "^3.2.0"
+ }
+ },
"node_modules/@electron-forge/maker-dmg": {
"version": "7.11.2",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-dmg/-/maker-dmg-7.11.2.tgz",
@@ -4127,6 +4145,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/fs-extra": {
+ "version": "9.0.13",
+ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
+ "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/har-format": {
"version": "1.2.16",
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
@@ -6210,6 +6239,242 @@
"node": ">= 22.12.0"
}
},
+ "node_modules/electron-installer-common": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.10.4.tgz",
+ "integrity": "sha512-8gMNPXfAqUE5CfXg8RL0vXpLE9HAaPkgLXVoHE3BMUzogMWenf4LmwQ27BdCUrEhkjrKl+igs2IHJibclR3z3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@electron/asar": "^3.2.5",
+ "@malept/cross-spawn-promise": "^1.0.0",
+ "debug": "^4.1.1",
+ "fs-extra": "^9.0.0",
+ "glob": "^7.1.4",
+ "lodash": "^4.17.15",
+ "parse-author": "^2.0.0",
+ "semver": "^7.1.1",
+ "tmp-promise": "^3.0.2"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/electron-userland/electron-installer-common?sponsor=1"
+ },
+ "optionalDependencies": {
+ "@types/fs-extra": "^9.0.1"
+ }
+ },
+ "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
+ "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/malept"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
+ }
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/electron-installer-common/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/electron-installer-debian": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz",
+ "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin",
+ "linux"
+ ],
+ "dependencies": {
+ "@malept/cross-spawn-promise": "^1.0.0",
+ "debug": "^4.1.1",
+ "electron-installer-common": "^0.10.2",
+ "fs-extra": "^9.0.0",
+ "get-folder-size": "^2.0.1",
+ "lodash": "^4.17.4",
+ "word-wrap": "^1.2.3",
+ "yargs": "^16.0.2"
+ },
+ "bin": {
+ "electron-installer-debian": "src/cli.js"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/@malept/cross-spawn-promise": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
+ "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/malept"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
+ }
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/electron-installer-debian/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/yargs": {
+ "version": "16.2.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
+ "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/electron-installer-debian/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/electron-installer-dmg": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/electron-installer-dmg/-/electron-installer-dmg-5.0.1.tgz",
@@ -7215,6 +7480,15 @@
"node": ">= 12"
}
},
+ "node_modules/gar": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz",
+ "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
@@ -7256,6 +7530,21 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-folder-size": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz",
+ "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "gar": "^1.0.4",
+ "tiny-each-async": "2.0.3"
+ },
+ "bin": {
+ "get-folder-size": "bin/get-folder-size"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -8682,6 +8971,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -11317,6 +11614,14 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/tiny-each-async": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz",
+ "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/tinyest": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/tinyest/-/tinyest-0.3.2.tgz",
@@ -11397,6 +11702,28 @@
"node": ">=0.6.0"
}
},
+ "node_modules/tmp-promise": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
+ "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tmp": "^0.2.0"
+ }
+ },
+ "node_modules/tmp-promise/node_modules/tmp": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
+ "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
"node_modules/tn1150": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tn1150/-/tn1150-0.1.0.tgz",
diff --git a/packaging/arch/PKGBUILD b/packaging/arch/PKGBUILD
new file mode 100644
index 00000000..aaeec8f8
--- /dev/null
+++ b/packaging/arch/PKGBUILD
@@ -0,0 +1,48 @@
+# Maintainer: Diffusion Studio
+# Packages the prebuilt Electron app produced by `electron-forge package`
+# into an Arch Linux package. The source tarball (and this file) are staged by
+# apps/desktop/scripts/stage-arch.mjs, which substitutes __VERSION__.
+
+pkgname=diffusion-studio
+pkgver=__VERSION__
+pkgrel=1
+pkgdesc="The professional video editor built for agents"
+arch=('x86_64')
+url="https://diffusion.studio"
+license=('MPL-2.0')
+# Electron's shared libraries ship pre-stripped release builds; stripping them
+# again yields gdb-add-index warnings and a useless -debug package.
+options=(!strip !debug)
+depends=(
+ 'gtk3'
+ 'nss'
+ 'libxss'
+ 'alsa-lib'
+ 'libxtst'
+ 'libxcursor'
+ 'libxrandr'
+ 'libxcomposite'
+ 'libxdamage'
+ 'libxfixes'
+ 'mesa'
+ 'at-spi2-core'
+ 'ca-certificates'
+)
+# Built locally from the forge package output; the tarball is staged next to
+# this file by stage-arch.mjs rather than downloaded from a URL.
+source=("diffusion-studio-$pkgver-linux-x64.tar.gz")
+sha256sums=('SKIP')
+
+package() {
+ cd "$srcdir/diffusion-studio-$pkgver-linux-x64"
+
+ install -d "$pkgdir/usr/lib/$pkgname"
+ cp -a . "$pkgdir/usr/lib/$pkgname/"
+
+ install -Dm755 launcher "$pkgdir/usr/bin/$pkgname"
+ install -Dm644 "$pkgname.desktop" "$pkgdir/usr/share/applications/$pkgname.desktop"
+ install -Dm644 "$pkgname.png" "$pkgdir/usr/share/pixmaps/$pkgname.png"
+
+ # The dapi CLI is shipped inside the app's resources; expose it on PATH.
+ ln -sf "/usr/lib/$pkgname/resources/cli/bin/dapi" "$pkgdir/usr/bin/dapi"
+}
diff --git a/packaging/arch/diffusion-studio.desktop b/packaging/arch/diffusion-studio.desktop
new file mode 100644
index 00000000..1c0bba7d
--- /dev/null
+++ b/packaging/arch/diffusion-studio.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Name=Diffusion Studio
+Comment=The professional video editor built for agents
+Exec=diffusion-studio %U
+Icon=diffusion-studio
+Terminal=false
+Type=Application
+Categories=AudioVideo;Video;Graphics;
+StartupWMClass=Diffusion Studio
+MimeType=x-scheme-handler/diffusion;
diff --git a/packaging/arch/launcher b/packaging/arch/launcher
new file mode 100644
index 00000000..6a884fb5
--- /dev/null
+++ b/packaging/arch/launcher
@@ -0,0 +1,5 @@
+#!/bin/sh
+# Launcher for the installed Arch package. The binary keeps its original name
+# ("Diffusion Studio", with a space), so this space-free entry point is what
+# /usr/bin/diffusion-studio and the .desktop Exec point at.
+exec "/usr/lib/diffusion-studio/Diffusion Studio" "$@"