5 Commits
Author SHA1 Message Date
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
kawa 3ed8f5683f Fixed publish script+added options to separate binaries publishing.
Bumped package.json to 2.0.0
2026-08-13 08:48:34 +02:00
9 changed files with 233 additions and 92 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
+2 -3
View File
@@ -1,5 +1,5 @@
# Motionity
### Demo available here : [motionity.kawa.zip](https://motionity.kawa.zip/)
Web-based motion graphics editor with keyframing, masking, filters and text animations.
This is a fork of the original [Motionity](https://github.com/alyssaxuu/motionity) by [@alyssaxuu](https://github.com/alyssaxuu), with bug fixes and enhancements.
@@ -22,8 +22,7 @@ npm run dev # Electron app
**Docker:**
```bash
npm run docker:build
npm run docker:run # http://localhost:8080
docker-compose up # http://localhost:8080
```
Full build instructions (Windows installers, Linux AppImage/Flatpak) in [PACKAGING.md](PACKAGING.md).
+1 -7
View File
@@ -1,12 +1,6 @@
services:
motionity:
build:
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
image: git.azuze.fr/kawa/motionity:latest
ports:
- "8080:8080"
restart: unless-stopped
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "motionity",
"productName": "Motionity",
"version": "1.0.0",
"version": "2.0.0",
"description": "Web-based motion graphics editor with keyframing, masking, filters and text animations",
"license": "MIT",
"author": "Kawa",
+35 -12
View File
@@ -36,14 +36,19 @@
./scripts/build-release.ps1 -Targets win -Tag v1.1.0
Windows installers only, named v1.1.0.
.EXAMPLE
./scripts/build-release.ps1 -Targets win,linux-appimage
Windows installers plus the Linux AppImage — no Flatpak.
.EXAMPLE
./scripts/build-release.ps1 -SkipVendor -SkipDeps
Reuse src/vendor/ and node_modules as they are — the fast rebuild.
#>
[CmdletBinding()]
param(
# Platforms to package. "win" is NSIS + portable, "linux" is AppImage + Flatpak.
[ValidateSet("win", "linux")]
# Platforms to package. "win" is NSIS + portable; "linux-appimage" and
# "linux-flatpak" are the two Linux bundles, and "linux" is shorthand for both.
[ValidateSet("win", "linux", "linux-appimage", "linux-flatpak")]
[string[]]$Targets = @("win", "linux"),
# Version used in the artifact names. Defaults to v<package.json version>,
@@ -64,11 +69,15 @@ param(
$ErrorActionPreference = "Stop"
function Invoke-Checked {
param([Parameter(Mandatory)][string]$Exe, [Parameter(Mandatory)][string[]]$Args)
Write-Host " > $Exe $($Args -join ' ')" -ForegroundColor DarkGray
& $Exe @Args
# NB: the param is $CmdArgs, not $Args. $Args is an automatic variable in
# PowerShell; a param of that name never binds the passed array (it stays the
# function's own empty $args), so `& $Exe @Args` would run the exe with no
# arguments — e.g. bare `npm`, which just prints usage and exits 1.
param([Parameter(Mandatory)][string]$Exe, [Parameter(Mandatory)][string[]]$CmdArgs)
Write-Host " > $Exe $($CmdArgs -join ' ')" -ForegroundColor DarkGray
& $Exe @CmdArgs
if ($LASTEXITCODE -ne 0) {
throw "'$Exe $($Args -join ' ')' failed with exit code $LASTEXITCODE."
throw "'$Exe $($CmdArgs -join ' ')' failed with exit code $LASTEXITCODE."
}
}
@@ -101,11 +110,20 @@ try {
Write-Host " output : $distDir"
Write-Host ""
# Expand "linux" to its two concrete bundles and drop duplicates, so the rest
# of the script only ever deals with win / linux-appimage / linux-flatpak.
$resolvedTargets = @()
foreach ($t in $Targets) {
if ($t -eq "linux") { $resolvedTargets += "linux-appimage", "linux-flatpak" }
else { $resolvedTargets += $t }
}
$resolvedTargets = @($resolvedTargets | Select-Object -Unique)
# electron-builder produces AppImage and Flatpak with Linux-only tooling
# (appimagetool, flatpak-builder). Warned rather than blocked: the same script
# runs under pwsh on a Linux box or in WSL, which is where that target belongs.
if ($Targets -contains "linux" -and $env:OS -eq "Windows_NT") {
Write-Warning "the linux target needs a Linux host or WSL — electron-builder cannot produce AppImage or Flatpak on Windows (PACKAGING.md has the WSL setup)."
if (($resolvedTargets -like "linux-*") -and $env:OS -eq "Windows_NT") {
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)."
}
if ($Clean) {
@@ -161,7 +179,7 @@ try {
throw 'electron-builder not found in node_modules — run "npm ci" (or drop -SkipDeps).'
}
foreach ($target in $Targets) {
foreach ($target in $resolvedTargets) {
Write-Host "Packaging $target..." -ForegroundColor Cyan
# --publish never: electron-builder otherwise tries to upload to whatever
@@ -175,10 +193,15 @@ try {
"-c.portable.artifactName=$(Get-ArtifactName "$prefix-win-x64-portable")"
)
}
"linux" {
"linux-appimage" {
$builderArgs = @(
"--linux", "AppImage", "flatpak", "--publish", "never",
"-c.appImage.artifactName=$(Get-ArtifactName "$prefix-linux-x86_64")",
"--linux", "AppImage", "--publish", "never",
"-c.appImage.artifactName=$(Get-ArtifactName "$prefix-linux-x86_64")"
)
}
"linux-flatpak" {
$builderArgs = @(
"--linux", "flatpak", "--publish", "never",
"-c.flatpak.artifactName=$(Get-ArtifactName "$prefix-linux-x86_64")"
)
}
+33 -15
View File
@@ -14,8 +14,10 @@
instead (creating the release if it does not exist).
-BinariesOnly ships just the installers: no docker build, no docker login, no
image push, and the release upload is implied. That is also the mode to use on
a Linux host or in WSL, where the AppImage and Flatpak targets actually build.
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
overrides -Targets. The Linux targets need a Linux host or WSL, which is where
-BinariesOnly linux-appimage,linux-flatpak belongs.
Credentials are read, in order of precedence:
1. -Username / -Password parameters
@@ -35,13 +37,19 @@
Full release: push the image and attach every dist/ installer to release v1.1.0.
.EXAMPLE
./scripts/publish.ps1 -BinariesOnly -Targets win -Tag v1.1.0
./scripts/publish.ps1 -BinariesOnly win -Tag v1.1.0
Windows installers only — build them and attach them to release v1.1.0. Docker
is never invoked, so this works with Docker Desktop stopped.
.EXAMPLE
./scripts/publish.ps1 -BinariesOnly -NoBinaryBuild -Tag v1.1.0
Retry a failed upload: attach the installers already in dist/ without rebuilding.
./scripts/publish.ps1 -BinariesOnly win,linux-appimage -Tag v1.1.0
Windows installers plus the Linux AppImage (no Flatpak), attached to v1.1.0.
.EXAMPLE
./scripts/publish.ps1 -BinariesOnly win -NoBinaryBuild -Tag v1.1.0
Retry a failed upload: attach the installers already in dist/ without rebuilding
(the target list is required syntactically but ignored — every dist/ installer
for the tag is uploaded regardless).
.EXAMPLE
./scripts/publish.ps1 -NoBinaries
@@ -92,8 +100,8 @@ param(
# Skip building the desktop installers.
[switch]$NoBinaries,
# Forwarded to build-release.ps1.
[ValidateSet("win", "linux")]
# Forwarded to build-release.ps1. "linux" is shorthand for both Linux bundles.
[ValidateSet("win", "linux", "linux-appimage", "linux-flatpak")]
[string[]]$Targets = @("win", "linux"),
[switch]$SkipVendor,
@@ -103,7 +111,10 @@ param(
# Ship only the installers: no docker build, login or push. Implies
# -PublishRelease, since building alone is what build-release.ps1 already does.
[switch]$BinariesOnly,
# Takes the target list to build (comma-separated), which overrides -Targets:
# -BinariesOnly win,linux-appimage
[ValidateSet("win", "linux-appimage", "linux-flatpak")]
[string[]]$BinariesOnly,
# Attach the installers to the Gitea release for $Tag, creating the release if
# it is missing.
@@ -122,11 +133,13 @@ param(
$ErrorActionPreference = "Stop"
function Invoke-Checked {
param([Parameter(Mandatory)][string]$Exe, [Parameter(Mandatory)][string[]]$Args)
Write-Host " > $Exe $($Args -join ' ')" -ForegroundColor DarkGray
& $Exe @Args
# $CmdArgs, not $Args: $Args is a PowerShell automatic variable and never
# binds the passed array, so `& $Exe @Args` would run the exe bare.
param([Parameter(Mandatory)][string]$Exe, [Parameter(Mandatory)][string[]]$CmdArgs)
Write-Host " > $Exe $($CmdArgs -join ' ')" -ForegroundColor DarkGray
& $Exe @CmdArgs
if ($LASTEXITCODE -ne 0) {
throw "'$Exe $($Args -join ' ')' failed with exit code $LASTEXITCODE."
throw "'$Exe $($CmdArgs -join ' ')' failed with exit code $LASTEXITCODE."
}
}
@@ -283,20 +296,25 @@ $repoRoot = Split-Path -Parent $PSScriptRoot
Push-Location $repoRoot
try {
# --- Mode resolution ------------------------------------------------------
if ($BinariesOnly -and $NoBinaries) {
# -BinariesOnly is a target list, so its mere presence (a non-empty array) is
# what selects the mode.
$binariesOnlyMode = $BinariesOnly.Count -gt 0
if ($binariesOnlyMode -and $NoBinaries) {
throw "-BinariesOnly and -NoBinaries cancel each other out — pick one."
}
if ($NoBinaryBuild -and $NoBinaries) {
throw "-NoBinaryBuild reuses the build that -NoBinaries skips entirely — pick one."
}
if ($BinariesOnly) {
if ($binariesOnlyMode) {
# Nothing to build, log into or push on the container side, and uploading
# is the whole point (build-release.ps1 alone covers "just build them").
$NoBuild = $true
$SkipLogin = $true
$PublishRelease = $true
# The targets named on -BinariesOnly are what to build.
$Targets = $BinariesOnly
}
$pushImage = -not $BinariesOnly
$pushImage = -not $binariesOnlyMode
if (-not $ReleaseRepo) { $ReleaseRepo = "$Owner/$Repo" }
if (-not $ApiBase) { $ApiBase = "https://$Registry" }
+17 -2
View File
@@ -103,6 +103,22 @@
<p class="header-2">Export this project</p>
<div id="export-project"><img src="assets/download-icon.svg"> <span>Export</span></div>
</div>
<div id="credits-modal">
<p class="header">Credits</p>
<p class="subtitle">Created by <span>Alyssa X</span></p>
<p class="subtitle">Maintained by <span>Kawa</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="color-picker"></div>
<div id="color-picker-fill"></div>
@@ -290,8 +306,6 @@
</div>
</div>
<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>
<img src="assets/replace-image.svg" id="replace-image">
<img src="assets/loading-image.svg" id="load-image" class="load-media">
@@ -375,6 +389,7 @@
<div id="share"><img src="assets/importexport.svg"> Import & export</div>
<div id="download"><img src="assets/download-icon.svg"> Download</div>
</div>
<div id="credits-button">Credits</div>
</div>
<video id="test-video"></video>
+8
View File
@@ -1785,6 +1785,14 @@ function 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() {
var value = $(this).val().toLowerCase();
if (value == '') {
+64 -52
View File
@@ -204,6 +204,27 @@ body {
.hand-active:hover {
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-canvas {
position: absolute;
@@ -212,58 +233,6 @@ body {
z-index: 9999999;
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 {
display: none !important;
}
@@ -1375,6 +1344,49 @@ input[type="number"] {
display: block;
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 {
color: var(--secondary-text-color);
font-family: Inter;