9 Commits
Author SHA1 Message Date
kawa 094bfbdc60 Updated links pointing to motionity.app > motionity.kawa.zip
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 16:00:46 +02:00
kawa 414e0ee30c Updated the credits with links to the contributor's git pages
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 15:33:51 +02:00
kawa cc439c1152 Modified publish pipeline for WSL (flatpak, appimage builds)
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 15:13:42 +02:00
kawa e775ea478f Made the README.md nicer
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 14:05:41 +02:00
kawa dcb804e48e Bumped package.json version
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 13:02:26 +02:00
kawa f380249c1c Merge branch 'main' of https://git.azuze.fr/kawa/Motionity
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 12:47:42 +02:00
kawaandClaude Haiku 4.5 a87716dc5b feat: add credits modal and update docker config
- Add credits modal showing project creators and libraries used
- Replace bottom-of-canvas credits with clickable credits button
- Update docker-compose to pull pre-built image from git.azuze.fr registry
- Simplify README Docker instructions

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-13 12:26:10 +02:00
kawa 109f737c2a Update README.md
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 11:43:11 +02:00
kawa d808c715fd Added the git wkflow
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
2026-08-13 09:04:04 +02:00
10 changed files with 649 additions and 115 deletions
+72
View File
@@ -0,0 +1,72 @@
name: Sync Gitea releases to GitHub
on:
push:
branches:
- main
workflow_dispatch: {}
env:
GITEA_URL: https://git.azuze.fr
GITEA_OWNER: kawa
GITEA_REPO: Motionity
jobs:
sync-releases:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- name: Fetch Gitea releases and sync to GitHub
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
AUTH_HEADER=()
if [ -n "${GITEA_TOKEN:-}" ]; then
AUTH_HEADER=(-H "Authorization: token ${GITEA_TOKEN}")
fi
# Récupère toutes les releases Gitea (pagination simple, 50 max, ajuster si besoin)
releases=$(curl -sf "${AUTH_HEADER[@]}" \
"${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases?limit=50")
echo "$releases" | jq -c '.[]' | while read -r release; do
tag=$(echo "$release" | jq -r '.tag_name')
name=$(echo "$release" | jq -r '.name // .tag_name')
body=$(echo "$release" | jq -r '.body // ""')
prerelease=$(echo "$release" | jq -r '.prerelease')
draft=$(echo "$release" | jq -r '.draft')
# Skip si la release existe déjà sur GitHub
if gh release view "$tag" >/dev/null 2>&1; then
echo "Release $tag existe déjà sur GitHub, on passe."
continue
fi
echo "Création de la release $tag sur GitHub..."
flags=()
[ "$prerelease" = "true" ] && flags+=(--prerelease)
[ "$draft" = "true" ] && flags+=(--draft)
# Le tag doit exister sur le repo GitHub ; s'il n'existe pas encore,
# on utilise --notes-file avec target par défaut (branche par défaut)
gh release create "$tag" \
--title "$name" \
--notes "$body" \
"${flags[@]}"
# Télécharge et attache les assets de la release Gitea
asset_count=$(echo "$release" | jq '.assets | length')
if [ "$asset_count" -gt 0 ]; then
tmpdir=$(mktemp -d)
echo "$release" | jq -c '.assets[]' | while read -r asset; do
asset_name=$(echo "$asset" | jq -r '.name')
asset_url=$(echo "$asset" | jq -r '.browser_download_url')
echo "Téléchargement de $asset_name..."
curl -sfL "${AUTH_HEADER[@]}" -o "${tmpdir}/${asset_name}" "$asset_url"
gh release upload "$tag" "${tmpdir}/${asset_name}" --clobber
done
rm -rf "$tmpdir"
fi
done
+84 -18
View File
@@ -69,6 +69,28 @@ Each `dist:*` script re-runs `vendor` and regenerates `build/icon.png`
(`scripts/make-icon.cjs` rasterises the logo geometry with zlib only — no (`scripts/make-icon.cjs` rasterises the logo geometry with zlib only — no
ImageMagick, no sharp). ImageMagick, no sharp).
### Why `desktopName` is `app.motionity.desktop.desktop`
The doubled suffix is correct, do not trim it. Electron reads the **root-level**
`desktopName` from `package.json` and derives the Wayland `app_id` / X11
`WM_CLASS` from it with the `.desktop` suffix stripped, so the value has to be
the desktop *file name*, not the app id. Flatpak in turn installs the entry as
`<appId>.desktop` and nothing can rename it — with `appId`
`app.motionity.desktop`, the file is `app.motionity.desktop.desktop`.
`linux.syncDesktopName: true` makes electron-builder use the same base name for
the AppImage's embedded entry and write a matching `StartupWMClass`. Without the
pair, the build warns
```
electron uses desktopName as app_id / WM_CLASS for window association.
reason=desktopName is not set in package.json
```
and the running window is not linked to its launcher entry: GNOME shows a
generic icon and a second, unpinnable dock item instead of the installed app.
Changing `appId` means changing `desktopName` in step with it.
### If the NSIS step fails with "Access denied" on `Motionity.exe` ### If the NSIS step fails with "Access denied" on `Motionity.exe`
On a locked-down Windows machine the security agent can take an exclusive lock On a locked-down Windows machine the security agent can take an exclusive lock
@@ -102,22 +124,58 @@ $env:ELECTRON_RUN_AS_NODE=$null; npm run dev # PowerShell
### Is WSL enough for AppImage and Flatpak? ### Is WSL enough for AppImage and Flatpak?
**AppImage: yes.** electron-builder produces the squashfs itself, so no FUSE is Yes, and `build-release.ps1 -UseWsl` does it for you from Windows:
needed at build time. To *run* the result inside WSL you need `libfuse2`
(or `./Motionity-1.0.0-x64.AppImage --appimage-extract-and-run`), and WSLg on
Windows 11 gives you the GUI.
**Flatpak: technically yes, practically annoying.** `flatpak-builder` runs under ```powershell
WSL2 (the kernel has the user namespaces and `/dev/fuse` that bubblewrap needs), ./scripts/build-release.ps1 -UseWsl # .exe on Windows, Linux bundles in WSL
but you must install the runtimes by hand first and there is no ./scripts/build-release.ps1 -Targets linux -UseWsl # Linux bundles only
`xdg-desktop-portal` to fall back on. If it fights you, build it in a Linux ./scripts/publish.ps1 -Tag v2.0.1 -PublishRelease -UseWsl # same, then attach to the Gitea release
container instead — it is the same command with fewer moving parts. ```
Both bundles have been built this way on this machine (`Ubuntu`, WSL2 kernel
6.18): a 172 MB AppImage and a 133 MB Flatpak, checksums verified with
`sha256sum -c`. Under the hood:
- the `npm ci`, `vendor` and icon steps run **once on the Windows side**, and the
WSL build reads them back through `/mnt/c` — one worktree, no second checkout;
- only `linux-appimage` and `linux-flatpak` are delegated. `-UseWsl` on a Linux
host, or with no Linux target, warns and changes nothing;
- the distro is the first installed one that is not `docker-desktop`, override
with `-WslDistro`. `docker-desktop` is skipped deliberately: it is Docker's own
LinuxKit VM, with no apt and no home to install the flatpak runtimes into, and
it is usually the *default* distro, so a blind `wsl --` lands there;
- missing tooling fails **before** the build with the apt or `flatpak install`
line to run, because electron-builder's own error for an absent flatpak ref is
a bare exit code naming neither the ref nor the remote.
**Why the build stages in `~/.cache/motionity-build` and not in `dist/`.**
electron-builder chmods every file it unpacks from the Electron zip, and `/mnt/c`
is mounted without the `metadata` option, so chmod is refused:
```
EPERM: operation not permitted, chmod '.../dist/linux-unpacked.tmp/locales/de.pak'
```
The alternative fix is `options = "metadata"` under `[automount]` in
`/etc/wsl.conf` plus a `wsl --shutdown` — a global, sudo-and-reboot change to the
distro. Building into ext4 and copying the two finished bundles back into `dist/`
needs neither, and is faster anyway. Reading `src/` over the mount is fine;
nothing chmods the input. The copy back is `cp -f`, never `cp -p` — preserving
modes means chmod, which is the EPERM being avoided.
**AppImage** needs no extra tooling: electron-builder downloads its own appimage
bundle and writes the squashfs itself, no FUSE at build time. To *run* the result
inside WSL you need `libfuse2` (or
`./motionity-*.AppImage --appimage-extract-and-run`); WSLg gives you the GUI.
**Flatpak** needs `flatpak-builder` and the runtimes installed by hand — the
WSL2 kernel has the user namespaces and `/dev/fuse` that bubblewrap wants, but
there is no `xdg-desktop-portal` to fall back on.
**`.exe`: no.** Build it on the Windows side. Cross-building NSIS from Linux **`.exe`: no.** Build it on the Windows side. Cross-building NSIS from Linux
needs Wine and rules out signing. needs Wine and rules out signing.
This machine currently has no WSL distro other than `docker-desktop`, so the One-time distro setup, from PowerShell:
Linux targets have not been run here. Setup, from PowerShell:
```powershell ```powershell
wsl --install -d Ubuntu wsl --install -d Ubuntu
@@ -127,7 +185,7 @@ Then inside Ubuntu:
```bash ```bash
sudo apt update sudo apt update
sudo apt install -y nodejs npm libfuse2 # AppImage sudo apt install -y nodejs npm libfuse2 # AppImage (libfuse2 only to run it)
sudo apt install -y flatpak flatpak-builder elfutils # Flatpak sudo apt install -y flatpak flatpak-builder elfutils # Flatpak
flatpak remote-add --if-not-exists --user flathub \ flatpak remote-add --if-not-exists --user flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo https://dl.flathub.org/repo/flathub.flatpakrepo
@@ -135,14 +193,22 @@ flatpak install --user -y flathub \
org.freedesktop.Platform//23.08 \ org.freedesktop.Platform//23.08 \
org.freedesktop.Sdk//23.08 \ org.freedesktop.Sdk//23.08 \
org.electronjs.Electron2.BaseApp//23.08 org.electronjs.Electron2.BaseApp//23.08
cd /mnt/c/Users/<you>/git\ azuze/motionity-2
npm install
npm run dist:linux
``` ```
Note that building on `/mnt/c` is slow. Copying the tree into the WSL Those three refs must match `build.flatpak.runtimeVersion` / `baseVersion` in
filesystem (`~/motionity`) is several times faster. `package.json`; `build-release.ps1` reads them from there when it checks.
To build inside the distro directly instead — no `-UseWsl`, and faster still,
since `src/` is read locally too:
```bash
git clone <repo> ~/motionity && cd ~/motionity
npm ci && npm run dist:linux
```
`wsl.exe` writes its own listings as UTF-16LE, which PowerShell 5.1 renders as
NUL-interleaved text — `wsl -l -v` can look like it has one distro when it has
two. `$env:WSL_UTF8 = "1"` fixes it.
## 2. Docker ## 2. Docker
+144 -17
View File
@@ -1,35 +1,162 @@
<div align="center">
<img src="src/assets/logo.svg" alt="Motionity" width="72">
# Motionity # Motionity
Web-based motion graphics editor with keyframing, masking, filters and text animations. **Web-based motion graphics editor** keyframing, masking, filters, text animations.
A free alternative to After Effects and Canva, running entirely in the browser.
This is a fork of the original [Motionity](https://github.com/alyssaxuu/motionity) by [@alyssaxuu](https://github.com/alyssaxuu), with bug fixes and enhancements. ### ▶ [Try the live demo — motionity.kawa.zip](https://motionity.kawa.zip/)
## Quick Start [![Demo](https://img.shields.io/badge/demo-motionity.kawa.zip-6c5ce7?style=flat-square)](https://motionity.kawa.zip/)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
[![Downloads](https://img.shields.io/badge/downloads-releases-2ea44f?style=flat-square)](https://git.azuze.fr/kawa/Motionity/releases)
---
Created by **[Alyssa X](https://github.com/alyssaxuu)** · maintained by **Kawa**
Fork of the original [Motionity](https://github.com/alyssaxuu/motionity), with bug fixes,
vendored (offline-capable) assets, desktop builds and a hardened container image.
</div>
---
## How to run
### Docker (recommended for self-hosting)
```bash
docker compose up -d # http://localhost:8080
```
Or without compose:
```bash
docker run --rm -p 8080:8080 git.azuze.fr/kawa/motionity:latest
```
Build the image yourself:
```bash
docker build -t motionity:latest . # ~86 MB on disk
docker build --build-arg WITH_FFMPEG=0 . # -23 MB, no MP4/GIF export
```
Runtime is [static-web-server](https://github.com/static-web-server/static-web-server) on Alpine — no Node, no shell, runs as UID 65534, `read_only` filesystem.
### Windows app
Grab the installer or the portable build from **[Releases](https://git.azuze.fr/kawa/Motionity/releases)**:
| File | What it is |
| --- | --- |
| `Motionity Setup <ver>.exe` | NSIS installer, per-user, choosable install dir |
| `Motionity-<ver>-x64.exe` | Portable, no install |
Build from source (on Windows):
**Web (localhost only):**
```bash ```bash
npm install npm install
npm run vendor npm run dist:win # -> dist/
```
### Linux app
From **[Releases](https://git.azuze.fr/kawa/Motionity/releases)**: `Motionity-<ver>-x86_64.AppImage` or the `.flatpak`.
```bash
chmod +x Motionity-*.AppImage && ./Motionity-*.AppImage # needs libfuse2
flatpak install ./Motionity-*.flatpak # or this
```
Build from source (on Linux or WSL2):
```bash
npm install
npm run dist:appimage # AppImage only
npm run dist:flatpak # Flatpak only
npm run dist:linux # both
```
### From source — web
```bash
npm install
npm run vendor # downloads third-party assets into src/vendor/ (~24 MB)
npm start # http://127.0.0.1:8080 npm start # http://127.0.0.1:8080
``` ```
**Desktop:** ```bash
HOST=0.0.0.0 PORT=3000 npm start # bind elsewhere
```
### From source — desktop
```bash ```bash
npm install npm install
npm run vendor npm run vendor
npm run dev # Electron app npm run dev # Electron
``` ```
**Docker:** > In a VS Code terminal, `ELECTRON_RUN_AS_NODE=1` breaks this. Unset it:
```bash > `$env:ELECTRON_RUN_AS_NODE=$null; npm run dev`
npm run docker:build
npm run docker:run # http://localhost:8080
```
Full build instructions (Windows installers, Linux AppImage/Flatpak) in [PACKAGING.md](PACKAGING.md). ### Two things to know
## Notes - **`npm run vendor` is mandatory** for every non-Docker target. Without it the page loads but every script 404s. The Docker build runs it inside the image.
- **Secure context required.** WebCodecs (fast export) and IndexedDB (project saving) only exist on `http://localhost` or HTTPS. Over plain HTTP on a LAN address they silently disappear: export falls back to slow real-time capture, projects stop saving. Anything beyond localhost needs TLS — `deploy/Caddyfile` is the shortest path.
- `ffmpeg.wasm` is vendored; `npm install` + `npm run vendor` are required Full packaging details, WSL notes and troubleshooting: **[PACKAGING.md](PACKAGING.md)**.
- WebCodecs and IndexedDB only work in secure context (`http://localhost` OK, plain HTTP over LAN is not)
- Serve over TLS for remote access ---
## Dependencies
Everything is vendored at build time — no CDN is contacted at runtime.
### Editor core
| Library | Role |
| --- | --- |
| [Fabric.js](https://github.com/fabricjs/fabric.js) | canvas object model, selection, transforms |
| [anime.js](https://github.com/juliangarnier/anime) | keyframe animation engine |
| [lottie-web](https://github.com/airbnb/lottie-web) | Lottie / Bodymovin playback |
| [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) | MP4 / GIF export (single-threaded [core](https://github.com/ffmpegwasm/ffmpeg.wasm-core)) |
| [webm-writer-js](https://github.com/thenickdude/webm-writer-js) | WEBM muxing for the WebCodecs exporter |
### UI
| Library | Role |
| --- | --- |
| [jQuery](https://github.com/jquery/jquery) | DOM plumbing |
| [Pickr](https://github.com/simonwep/pickr) | color picker |
| [Selection.js](https://github.com/simonwep/selection) | timeline box-selection |
| [Sortable](https://github.com/SortableJS/Sortable) | layer reordering |
| [jquery-nice-select](https://github.com/hernansartorio/jquery-nice-select) | styled `<select>` |
| range-slider | timeline / property sliders (vendored, no upstream banner) |
| [Localbase](https://github.com/dannyconnell/localbase) | IndexedDB project storage |
| [webfontloader](https://github.com/typekit/webfontloader) | Google Fonts loading |
| [Inter](https://github.com/rsms/inter) | UI typeface |
### Build & runtime
| Package | Role |
| --- | --- |
| [Electron](https://github.com/electron/electron) | desktop shell |
| [electron-builder](https://github.com/electron-userland/electron-builder) | NSIS / AppImage / Flatpak packaging |
| [static-web-server](https://github.com/static-web-server/static-web-server) | container HTTP server |
`scripts/server.cjs` (the `npm start` server) has zero dependencies — Node 18+ builtins only.
### Still online by design
[Google Fonts](https://fonts.google.com/) (font picker), [Pixabay](https://pixabay.com/) (image/video/audio search), [Unsplash](https://unsplash.com/) (empty-state samples). All three degrade quietly when offline; the editor itself starts and exports without a network.
---
## License
[MIT](LICENSE) — same as upstream.
+1 -7
View File
@@ -1,12 +1,6 @@
services: services:
motionity: motionity:
build: image: git.azuze.fr/kawa/motionity:latest
context: .
# WITH_FFMPEG: "0" trims 18.5 MB by fetching the MP4/GIF encoder from
# archive.org at runtime instead of shipping it.
args:
WITH_FFMPEG: "1"
image: motionity:latest
ports: ports:
- "8080:8080" - "8080:8080"
restart: unless-stopped restart: unless-stopped
+4 -1
View File
@@ -1,7 +1,8 @@
{ {
"name": "motionity", "name": "motionity",
"productName": "Motionity", "productName": "Motionity",
"version": "2.0.0", "version": "2.0.1",
"desktopName": "app.motionity.desktop.desktop",
"description": "Web-based motion graphics editor with keyframing, masking, filters and text animations", "description": "Web-based motion graphics editor with keyframing, masking, filters and text animations",
"license": "MIT", "license": "MIT",
"author": "Kawa", "author": "Kawa",
@@ -16,6 +17,7 @@
"dist:appimage": "npm run vendor && npm run icons && electron-builder --linux AppImage", "dist:appimage": "npm run vendor && npm run icons && electron-builder --linux AppImage",
"dist:flatpak": "npm run vendor && npm run icons && electron-builder --linux flatpak", "dist:flatpak": "npm run vendor && npm run icons && electron-builder --linux flatpak",
"dist:linux": "npm run vendor && npm run icons && electron-builder --linux AppImage flatpak", "dist:linux": "npm run vendor && npm run icons && electron-builder --linux AppImage flatpak",
"dist:linux:wsl": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/build-release.ps1 -Targets linux -UseWsl",
"docker:build": "docker build -t motionity:latest .", "docker:build": "docker build -t motionity:latest .",
"docker:run": "docker run --rm -p 8080:8080 motionity:latest", "docker:run": "docker run --rm -p 8080:8080 motionity:latest",
"release:build": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/build-release.ps1", "release:build": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/build-release.ps1",
@@ -78,6 +80,7 @@
"icon": "build/icon.png", "icon": "build/icon.png",
"category": "Graphics", "category": "Graphics",
"synopsis": "Motion graphics editor", "synopsis": "Motion graphics editor",
"syncDesktopName": true,
"desktop": { "desktop": {
"entry": { "entry": {
"Name": "Motionity", "Name": "Motionity",
+223 -5
View File
@@ -28,10 +28,20 @@
Windows builds the .exe targets; AppImage and Flatpak need a Linux host or Windows builds the .exe targets; AppImage and Flatpak need a Linux host or
WSL (see PACKAGING.md). Nothing here cross-builds. WSL (see PACKAGING.md). Nothing here cross-builds.
-UseWsl makes that split automatic: the .exe targets run on Windows, and the
Linux ones are handed to a WSL distro over /mnt/c, against this same worktree.
The npm install, the vendor step and the icon all happen once on the Windows
side and the WSL build reads them through the mount, so the artifacts still
land in dist/ and the checksum step below sees every one of them.
.EXAMPLE .EXAMPLE
./scripts/build-release.ps1 ./scripts/build-release.ps1
Build every target at v<package.json version>. Build every target at v<package.json version>.
.EXAMPLE
./scripts/build-release.ps1 -UseWsl
Same, with AppImage and Flatpak built in the first non-docker WSL distro.
.EXAMPLE .EXAMPLE
./scripts/build-release.ps1 -Targets win -Tag v1.1.0 ./scripts/build-release.ps1 -Targets win -Tag v1.1.0
Windows installers only, named v1.1.0. Windows installers only, named v1.1.0.
@@ -40,6 +50,10 @@
./scripts/build-release.ps1 -Targets win,linux-appimage ./scripts/build-release.ps1 -Targets win,linux-appimage
Windows installers plus the Linux AppImage — no Flatpak. Windows installers plus the Linux AppImage — no Flatpak.
.EXAMPLE
./scripts/build-release.ps1 -Targets linux -UseWsl -WslDistro Ubuntu-24.04
Both Linux bundles, in a named distro.
.EXAMPLE .EXAMPLE
./scripts/build-release.ps1 -SkipVendor -SkipDeps ./scripts/build-release.ps1 -SkipVendor -SkipDeps
Reuse src/vendor/ and node_modules as they are — the fast rebuild. Reuse src/vendor/ and node_modules as they are — the fast rebuild.
@@ -62,6 +76,14 @@ param(
# Skip the npm install even when node_modules is missing. # Skip the npm install even when node_modules is missing.
[switch]$SkipDeps, [switch]$SkipDeps,
# Build the Linux targets inside WSL instead of warning that they cannot be
# built on Windows. Ignored on a Linux host, where they build natively.
[switch]$UseWsl,
# WSL distro to build in. Defaults to the first installed one that is not
# docker-desktop.
[string]$WslDistro,
# Remove dist/ before building. # Remove dist/ before building.
[switch]$Clean [switch]$Clean
) )
@@ -91,6 +113,153 @@ function Get-ArtifactName {
return $Stem + '.${ext}' return $Stem + '.${ext}'
} }
# --- WSL plumbing -------------------------------------------------------------
function Invoke-Wsl {
param([Parameter(Mandatory)][string]$Distro, [Parameter(Mandatory)][string]$Command)
Write-Host " > wsl -d $Distro -- $Command" -ForegroundColor DarkGray
# bash -lc, so PATH matches an interactive shell: node installed through nvm
# or fnm is not on the default non-login PATH.
& wsl.exe -d $Distro -e bash -lc $Command
if ($LASTEXITCODE -ne 0) {
throw "the WSL build in '$Distro' failed with exit code $LASTEXITCODE."
}
}
function Test-WslCommand {
param([Parameter(Mandatory)][string]$Distro, [Parameter(Mandatory)][string]$Command)
& wsl.exe -d $Distro -e bash -lc $Command *> $null
return ($LASTEXITCODE -eq 0)
}
function ConvertTo-BashArg {
<#
Single quotes, always. The artifactName arguments carry a literal ${ext}
for electron-builder to expand, and bash would expand it to nothing first
inside double quotes; the repo path has a space in it.
#>
param([Parameter(Mandatory)][string]$Value)
return "'" + $Value.Replace("'", "'\''") + "'"
}
function Get-WslDistro {
<#
docker-desktop is excluded on purpose. It is Docker Desktop's own LinuxKit
VM: no apt, no user home to install the flatpak runtimes into — and it is
usually the *default* distro, so picking blind would land there.
#>
param([string]$Requested)
if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) {
throw "-UseWsl needs wsl.exe on PATH. Install a distro with 'wsl --install -d Ubuntu' (PACKAGING.md has the rest of the setup)."
}
# wsl.exe writes its own listings as UTF-16LE, which PowerShell 5.1 reads back
# as NUL-interleaved text. WSL_UTF8 fixes it at the source; the -replace is the
# fallback for WSL older than 0.64.
$previousUtf8 = $env:WSL_UTF8
$env:WSL_UTF8 = "1"
try { $listed = (& wsl.exe --list --quiet) -join "`n" }
finally { $env:WSL_UTF8 = $previousUtf8 }
$distros = @(($listed -replace "`0", "") -split "`r?`n" |
ForEach-Object { $_.Trim() } | Where-Object { $_ })
if ($Requested) {
if ($distros -notcontains $Requested) {
throw "WSL distro '$Requested' is not installed. Installed: $($distros -join ', ')."
}
return $Requested
}
$usable = @($distros | Where-Object { $_ -notlike "docker-desktop*" })
if (-not $usable.Count) {
throw "no WSL distro that can build here (installed: $($distros -join ', ')). Run 'wsl --install -d Ubuntu', then the package setup in PACKAGING.md."
}
return $usable[0]
}
function Get-WslPath {
param([Parameter(Mandatory)][string]$Distro, [Parameter(Mandatory)][string]$WindowsPath)
# -e wslpath rather than a shell: the backslashes and the space in the repo
# path then reach wslpath as one literal argv entry, unquoted and unmangled.
$out = (& wsl.exe -d $Distro -e wslpath -a -u $WindowsPath)
$path = (@($out) -join "").Replace("`0", "").Trim()
if ($LASTEXITCODE -ne 0 -or -not $path) {
throw "wslpath failed in '$Distro' for '$WindowsPath' — is the Windows drive mounted in that distro?"
}
return $path
}
function Get-WslStageDir {
<#
Where the Linux build actually happens. It cannot be dist/ on /mnt/c:
electron-builder chmods every file it unpacks out of the Electron zip, and
drvfs answers chmod with EPERM unless /mnt/c was mounted with the metadata
option — a global change to the distro needing sudo and a wsl --shutdown.
EPERM: operation not permitted, chmod '.../linux-unpacked.tmp/locales/de.pak'
Staging in the distro's own filesystem and copying the finished bundles back
needs none of that, and is faster besides. Reading src/ over the mount is
still fine — nothing chmods the input.
#>
param([Parameter(Mandatory)][string]$Distro)
# printf, not echo: no trailing newline to trim off the path.
$out = (& wsl.exe -d $Distro -e bash -lc 'printf %s "${XDG_CACHE_HOME:-$HOME/.cache}/motionity-build"')
$path = (@($out) -join "").Replace("`0", "").Trim()
if ($LASTEXITCODE -ne 0 -or $path -notlike "/*") {
throw "could not resolve a staging directory in '$Distro' (got '$path')."
}
return $path
}
function Assert-WslBuildEnv {
<#
Fail before the build rather than during it. electron-builder's own error
for a missing flatpak ref is a bare flatpak-builder exit code that names
neither the ref nor the remote.
#>
param(
[Parameter(Mandatory)][string]$Distro,
[Parameter(Mandatory)][string[]]$WslTargets,
[Parameter(Mandatory)]$Pkg
)
if (-not (Test-WslCommand $Distro 'command -v node')) {
throw "no node in WSL distro '$Distro'. Inside it: sudo apt update && sudo apt install -y nodejs npm"
}
# AppImage needs nothing else: electron-builder downloads its own appimage
# tooling and writes the squashfs itself. libfuse2 is only needed to *run* the
# result, which is not this script's job.
if ($WslTargets -notcontains "linux-flatpak") { return }
if (-not (Test-WslCommand $Distro 'command -v flatpak-builder')) {
throw "no flatpak-builder in WSL distro '$Distro'. Inside it: sudo apt install -y flatpak flatpak-builder elfutils"
}
$runtimeVersion = $Pkg.build.flatpak.runtimeVersion
$baseVersion = $Pkg.build.flatpak.baseVersion
if (-not $runtimeVersion) { $runtimeVersion = "23.08" }
if (-not $baseVersion) { $baseVersion = $runtimeVersion }
$refs = @(
"org.freedesktop.Platform//$runtimeVersion",
"org.freedesktop.Sdk//$runtimeVersion",
"org.electronjs.Electron2.BaseApp//$baseVersion"
)
$missing = @($refs | Where-Object { -not (Test-WslCommand $Distro "flatpak info $_") })
if ($missing.Count) {
throw @"
flatpak refs missing in '$Distro': $($missing -join ', ')
Inside the distro:
flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub $($refs -join ' ')
"@
}
}
$repoRoot = Split-Path -Parent $PSScriptRoot $repoRoot = Split-Path -Parent $PSScriptRoot
Push-Location $repoRoot Push-Location $repoRoot
try { try {
@@ -119,11 +288,37 @@ try {
} }
$resolvedTargets = @($resolvedTargets | Select-Object -Unique) $resolvedTargets = @($resolvedTargets | Select-Object -Unique)
# electron-builder produces AppImage and Flatpak with Linux-only tooling # electron-builder produces AppImage and Flatpak with Linux-only tooling (its
# (appimagetool, flatpak-builder). Warned rather than blocked: the same script # downloaded appimage bundle, and flatpak-builder). -UseWsl hands those two
# runs under pwsh on a Linux box or in WSL, which is where that target belongs. # targets to a WSL distro; without it they stay a warning rather than an error,
if (($resolvedTargets -like "linux-*") -and $env:OS -eq "Windows_NT") { # because the other right answer is running this whole script under pwsh on a
Write-Warning "the Linux targets need a Linux host or WSL — electron-builder cannot produce AppImage or Flatpak on Windows (PACKAGING.md has the WSL setup)." # Linux box, where they build natively.
$onWindows = ($env:OS -eq "Windows_NT")
$wslTargets = @($resolvedTargets | Where-Object { $_ -like "linux-*" })
$useWslHere = $onWindows -and $UseWsl -and [bool]$wslTargets.Count
if ($onWindows -and $wslTargets.Count -and -not $UseWsl) {
Write-Warning "the Linux targets need a Linux host or WSL — electron-builder cannot produce AppImage or Flatpak on Windows. Add -UseWsl to build them in a WSL distro (PACKAGING.md has the setup)."
}
if ($UseWsl -and -not $onWindows) {
Write-Warning "-UseWsl ignored: this is already a Linux host, so the Linux targets build natively."
}
if ($UseWsl -and $onWindows -and -not $wslTargets.Count) {
Write-Warning "-UseWsl ignored: no Linux target was requested (-Targets $($Targets -join ', '))."
}
$wslRepo = $null
$wslStage = $null
if ($useWslHere) {
$WslDistro = Get-WslDistro -Requested $WslDistro
$wslRepo = Get-WslPath -Distro $WslDistro -WindowsPath $repoRoot
$wslStage = Get-WslStageDir -Distro $WslDistro
Write-Host "Linux targets go to WSL" -ForegroundColor Cyan
Write-Host " distro : $WslDistro"
Write-Host " worktree: $wslRepo"
Write-Host " staging : $wslStage (copied back into dist/)"
Assert-WslBuildEnv -Distro $WslDistro -WslTargets $wslTargets -Pkg $pkg
Write-Host ""
} }
if ($Clean) { if ($Clean) {
@@ -207,7 +402,30 @@ try {
} }
} }
if ($useWslHere -and $target -like "linux-*") {
# `node cli.js` rather than node_modules/.bin/electron-builder: the
# extensionless shim npm writes on Windows is a sh script, and whether
# it is executable across the mount depends on the drvfs options.
#
# The output goes to the distro's filesystem (see Get-WslStageDir) and
# the bundles are copied back afterwards. `cp -f`, never `cp -p`:
# preserving modes means chmod, which is the EPERM this avoids.
#
# The rm clears this tag's previous bundles from the staging directory,
# so the copy back cannot pick up an artifact from an earlier build that
# electron-builder did not overwrite this time round.
$quotedArgs = @($builderArgs | ForEach-Object { ConvertTo-BashArg $_ }) -join " "
$stage = ConvertTo-BashArg $wslStage
$wslCommand = "cd $(ConvertTo-BashArg $wslRepo)" +
" && mkdir -p $stage" +
" && rm -f $stage/$prefix-*" +
" && USE_HARD_LINKS=false node node_modules/electron-builder/cli.js $quotedArgs $(ConvertTo-BashArg "-c.directories.output=$wslStage")" +
" && cp -f $stage/$prefix-* $(ConvertTo-BashArg "$wslRepo/dist")/"
Invoke-Wsl -Distro $WslDistro -Command $wslCommand
}
else {
Invoke-Checked $builder $builderArgs Invoke-Checked $builder $builderArgs
}
Write-Host "" Write-Host ""
} }
+23 -3
View File
@@ -16,8 +16,8 @@
-BinariesOnly ships just the installers: no docker build, no docker login, no -BinariesOnly ships just the installers: no docker build, no docker login, no
image push, and the release upload is implied. It takes the target list to image push, and the release upload is implied. It takes the target list to
build (win, linux-appimage, linux-flatpak — comma-separated) and that list build (win, linux-appimage, linux-flatpak — comma-separated) and that list
overrides -Targets. The Linux targets need a Linux host or WSL, which is where overrides -Targets. The Linux targets need a Linux host or WSL: on Windows,
-BinariesOnly linux-appimage,linux-flatpak belongs. add -UseWsl and build-release.ps1 hands them to a WSL distro.
Credentials are read, in order of precedence: Credentials are read, in order of precedence:
1. -Username / -Password parameters 1. -Username / -Password parameters
@@ -45,6 +45,11 @@
./scripts/publish.ps1 -BinariesOnly win,linux-appimage -Tag v1.1.0 ./scripts/publish.ps1 -BinariesOnly win,linux-appimage -Tag v1.1.0
Windows installers plus the Linux AppImage (no Flatpak), attached to v1.1.0. Windows installers plus the Linux AppImage (no Flatpak), attached to v1.1.0.
.EXAMPLE
./scripts/publish.ps1 -Tag v1.1.0 -PublishRelease -UseWsl
Full release from Windows: image push, .exe installers built natively, AppImage
and Flatpak built in WSL, everything attached to release v1.1.0.
.EXAMPLE .EXAMPLE
./scripts/publish.ps1 -BinariesOnly win -NoBinaryBuild -Tag v1.1.0 ./scripts/publish.ps1 -BinariesOnly win -NoBinaryBuild -Tag v1.1.0
Retry a failed upload: attach the installers already in dist/ without rebuilding Retry a failed upload: attach the installers already in dist/ without rebuilding
@@ -105,6 +110,11 @@ param(
[string[]]$Targets = @("win", "linux"), [string[]]$Targets = @("win", "linux"),
[switch]$SkipVendor, [switch]$SkipVendor,
# Forwarded to build-release.ps1: build the Linux targets in a WSL distro
# instead of warning that Windows cannot produce them.
[switch]$UseWsl,
[string]$WslDistro,
# Reuse the installers already in dist/ instead of re-running the build. For # Reuse the installers already in dist/ instead of re-running the build. For
# retrying a failed upload without paying for the build again. # retrying a failed upload without paying for the build again.
[switch]$NoBinaryBuild, [switch]$NoBinaryBuild,
@@ -413,7 +423,17 @@ try {
# `& script.ps1` leaves $LASTEXITCODE untouched, and with -NoBuild no # `& script.ps1` leaves $LASTEXITCODE untouched, and with -NoBuild no
# docker command has reset it, so checking it would rethrow whatever the # docker command has reset it, so checking it would rethrow whatever the
# caller's shell last failed at. # caller's shell last failed at.
& (Join-Path $PSScriptRoot "build-release.ps1") -Tag $Tag -Targets $Targets -SkipVendor:$SkipVendor # -WslDistro is only passed when set: build-release.ps1 treats an empty
# string as "not requested" either way, but splatting nothing keeps the
# -WhatIf/-Verbose trace readable.
$buildParams = @{
Tag = $Tag
Targets = $Targets
SkipVendor = $SkipVendor
UseWsl = $UseWsl
}
if ($WslDistro) { $buildParams["WslDistro"] = $WslDistro }
& (Join-Path $PSScriptRoot "build-release.ps1") @buildParams
} }
$artifacts = @(Get-ChildItem $distDir -Filter "motionity-$Tag-*" -File | ForEach-Object FullName) $artifacts = @(Get-ChildItem $distDir -Filter "motionity-$Tag-*" -File | ForEach-Object FullName)
+21 -7
View File
@@ -19,17 +19,17 @@
<!-- Open Graph / Facebook --> <!-- Open Graph / Facebook -->
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:url" content="https://www.motionity.app/"> <meta property="og:url" content="https://motionity.kawa.zip/">
<meta property="og:title" content="Motionity - The web-based motion graphics editor for everyone"> <meta property="og:title" content="Motionity - The web-based motion graphics editor for everyone">
<meta property="og:description" content="Create animated videos for free with Motionity, an open source motion graphics editor with keyframing, masking, filters, text animations, and more. "> <meta property="og:description" content="Create animated videos for free with Motionity, an open source motion graphics editor with keyframing, masking, filters, text animations, and more. ">
<meta property="og:image" content="https://motionity.app/meta.png"> <meta property="og:image" content="https://motionity.kawa.zip/meta.png">
<!-- Twitter --> <!-- Twitter -->
<meta property="twitter:card" content="summary_large_image"> <meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://www.motionity.app/"> <meta property="twitter:url" content="https://motionity.kawa.zip/">
<meta property="twitter:title" content="Motionity - The web-based motion graphics editor for everyone"> <meta property="twitter:title" content="Motionity - The web-based motion graphics editor for everyone">
<meta property="twitter:description" content="Create animated videos for free with Motionity, an open source motion graphics editor with keyframing, masking, filters, text animations, and more. "> <meta property="twitter:description" content="Create animated videos for free with Motionity, an open source motion graphics editor with keyframing, masking, filters, text animations, and more. ">
<meta property="twitter:image" content="https://motionity.app/meta.png"> <meta property="twitter:image" content="https://motionity.kawa.zip/meta.png">
</head> </head>
<body draggable="false"> <body draggable="false">
<div id="disclaimer"> <div id="disclaimer">
@@ -37,7 +37,6 @@
<div id="emoji">🤔</div> <div id="emoji">🤔</div>
<div id="opt-title">Motionity isn't optimized for mobile</div> <div id="opt-title">Motionity isn't optimized for mobile</div>
<div id="opt-desc">You need to use a computer to be able to create animations with Motionity.</div> <div id="opt-desc">You need to use a computer to be able to create animations with Motionity.</div>
<a href="https://twitter.com/alyssaxuu" target="_blank" id="opt-button">Other products by the maker</a>
</div> </div>
<div id="disc-overlay"></div> <div id="disc-overlay"></div>
</div> </div>
@@ -103,6 +102,22 @@
<p class="header-2">Export this project</p> <p class="header-2">Export this project</p>
<div id="export-project"><img src="assets/download-icon.svg"> <span>Export</span></div> <div id="export-project"><img src="assets/download-icon.svg"> <span>Export</span></div>
</div> </div>
<div id="credits-modal">
<p class="header">Credits</p>
<p class="subtitle">Created by <span><a href="https://github.com/alyssaxuu">Alyssa X</a></span></p>
<p class="subtitle">Current version maintained by <span><a href="https://github.com/KawaKode/Motionity">Kawa</a></span></p>
<hr style="margin-top: 15px; margin-bottom: 15px;">
<p class="subheader">Libraries & Tools</p>
<div id="credits-list">
<div class="credit-item">Fabric.js</div>
<div class="credit-item">Anime.js</div>
<div class="credit-item">Lottie</div>
<div class="credit-item">Pickr</div>
<div class="credit-item">Sortable.js</div>
<div class="credit-item">FFmpeg.wasm</div>
<div class="credit-item">jQuery</div>
</div>
</div>
<div id="background-overlay"></div> <div id="background-overlay"></div>
<div id="color-picker"></div> <div id="color-picker"></div>
<div id="color-picker-fill"></div> <div id="color-picker-fill"></div>
@@ -290,8 +305,6 @@
</div> </div>
</div> </div>
<div id="bottom-canvas"> <div id="bottom-canvas">
<a id="sponsor" href="https://github.com/sponsors/alyssaxuu" target="_blank"><img src="assets/sponsor.svg"> Sponsor</a>
<a id="alyssa-credit" href="https://twitter.com/alyssaxuu" target="_blank">Made by <span>Alyssa X</span> <img src="assets/alyssaimg.jpeg"></a>
</div> </div>
<img src="assets/replace-image.svg" id="replace-image"> <img src="assets/replace-image.svg" id="replace-image">
<img src="assets/loading-image.svg" id="load-image" class="load-media"> <img src="assets/loading-image.svg" id="load-image" class="load-media">
@@ -375,6 +388,7 @@
<div id="share"><img src="assets/importexport.svg"> Import & export</div> <div id="share"><img src="assets/importexport.svg"> Import & export</div>
<div id="download"><img src="assets/download-icon.svg"> Download</div> <div id="download"><img src="assets/download-icon.svg"> Download</div>
</div> </div>
<div id="credits-button">Credits</div>
</div> </div>
<video id="test-video"></video> <video id="test-video"></video>
+8
View File
@@ -1785,6 +1785,14 @@ function importExportModal() {
} }
$('#share').on('click', importExportModal); $('#share').on('click', importExportModal);
// Open credits modal
function creditsModal() {
hideModals();
$('#credits-modal').toggleClass('modal-open');
$('#background-overlay').toggleClass('modal-open');
}
$('#credits-button').on('click', creditsModal);
function searchInput() { function searchInput() {
var value = $(this).val().toLowerCase(); var value = $(this).val().toLowerCase();
if (value == '') { if (value == '') {
+64 -52
View File
@@ -204,6 +204,27 @@ body {
.hand-active:hover { .hand-active:hover {
cursor: pointer; cursor: pointer;
} }
#credits-button {
width: 100px;
position: absolute;
right: 330px;
bottom: 12px;
height: 38px;
line-height: 38px;
text-align: center;
background-color: #30314e;
color: var(--main-text-color);
border-radius: 5px;
box-shadow: inset 0px 2px 2px rgba(255, 255, 255, 0.05),
inset 0px -2px 1px rgba(0, 0, 0, 0.05);
font-family: Inter;
font-weight: 500;
font-size: 14px;
}
#credits-button:hover {
cursor: pointer;
opacity: 0.7;
}
/* Bottom of canvas */ /* Bottom of canvas */
#bottom-canvas { #bottom-canvas {
position: absolute; position: absolute;
@@ -212,58 +233,6 @@ body {
z-index: 9999999; z-index: 9999999;
width: 100%; width: 100%;
} }
#sponsor {
position: absolute;
bottom: 10px;
left: -10px;
height: 32px;
line-height: 32px;
font-family: Inter;
font-size: 14px;
font-weight: 500;
color: var(--accent-color);
background: rgba(22, 95, 205, 0.1);
text-align: center;
text-decoration: none;
padding-left: 10px;
padding-right: 10px;
border-radius: 5px;
}
#sponsor img {
margin-right: 3px;
margin-bottom: -2px;
}
#sponsor:hover {
background: rgba(22, 95, 205, 0.3) !important;
}
#alyssa-credit {
color: var(--secondary-text-color);
font-size: 14px;
font-weight: 500;
position: absolute;
right: -20px;
bottom: 15px;
font-family: Inter;
width: 200px;
filter: drop-shadow(0px 1px 15px #141629);
text-decoration: none !important;
}
#alyssa-credit:hover {
filter: drop-shadow(0px 1px 10px #141629) !important;
}
#alyssa-credit span {
color: var(--main-text-color) !important;
text-decoration: none !important;
display: inline-block;
}
#alyssa-credit img {
display: inline-block;
margin-left: 5px;
border-radius: 50%;
vertical-align: middle;
width: 18px;
margin-top: -2px;
}
.hide-folder { .hide-folder {
display: none !important; display: none !important;
} }
@@ -1375,6 +1344,49 @@ input[type="number"] {
display: block; display: block;
z-index: 99999999999; z-index: 99999999999;
} }
/* Credits modal */
#credits-modal {
width: 300px;
max-height: 400px;
background-color: var(--panel-back);
border: 1px solid var(--panel-stroke);
border-radius: 5px;
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.3);
position: absolute;
right: 280px;
bottom: 60px;
visibility: hidden;
display: block;
z-index: 99999999999;
padding-bottom: 15px;
overflow-y: auto;
}
#credits-modal .header {
margin-bottom: 15px;
}
#credits-modal .subtitle {
margin-left: 20px;
margin-bottom: 5px;
font-size: 13px;
}
#credits-modal .subtitle span {
color: var(--main-text-color) !important;
font-weight: 500;
}
#credits-modal .subheader {
margin-top: 10px;
margin-bottom: 10px;
}
#credits-list {
margin-left: 20px;
margin-right: 20px;
}
.credit-item {
color: var(--secondary-text-color);
font-family: Inter;
font-size: 13px;
margin-bottom: 8px;
}
.subtitle { .subtitle {
color: var(--secondary-text-color); color: var(--secondary-text-color);
font-family: Inter; font-family: Inter;