iArt.ai
AI motion graphics software — the After Effects alternative anyone can use. Create animations and explainer videos from a text prompt in minutes.
Turn your app screenshots into a polished, animated promo video in minutes — device frames, motion, and music, exported straight to MP4.
MediaTypeScript · CSSGitHub @Shokh977OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Turns app screenshots into animated promo videos, rendered on a canvas in the browser. Next.js on Vercel, Supabase (auth + Postgres), Cloudflare R2 (files), Paddle (billing). Architecture, conventions and every dashboard setting live in CLAUDE.md; this file covers running it and operating it.
cp .env.example .env.local # fill in values (see comments in the file)
npm install
npm run dev # http://localhost:3000
npm run visual-regression # after any rendering change (see CLAUDE.md rule 6)
Free accounts can store 200 MB of files (screenshots, icons, music), Pro
accounts 5 GB (maxStorageBytes in src/lib/plan.ts). The limit is
enforced on the server when an upload is approved
(/api/storage/upload → public.reserve_storage, migration 0019), so it
can't be bypassed from the browser. Usage shows on /account, on
/projects and in the editor's slide rail. Deleting a project frees its
space immediately; files a project no longer uses (replaced screenshots,
deleted slides) are cleaned up an hour after they stop being used,
whenever the user's usage is recounted.
After running migration 0019 once, record files that already existed:
node --env-file=.env.local scripts/backfill-storage-usage.mjs.
Every Sunday at 03:17 UTC, .github/workflows/db-backup.yml:
Backs up production with pg_dump (scripts/backup/backup-db.mjs) to
the private R2 bucket nimina-backups, under weekly/<YYYY-MM-DD>/:
data.sql.gz: every row of every table in public and auth (users,
sign-in identities, projects, billing records, …);public-schema.sql.gz: our schema exactly as it was in production;supabase-schemas.sql.gz: Supabase's own auth/storage schemas,
used only by the restore check below;manifest.json: row count and content checksum of every table, taken
in the same database snapshot as the dump.It keeps the newest 8 backups and deletes older ones.
Proves the backup restores (scripts/backup/verify-backup.mjs). It
downloads the backup it just uploaded, runs the restore procedure below
into a throwaway Postgres 17, and compares every table's row count and
contents with the manifest. Any difference fails the run, and GitHub
emails the repo owner. The result is saved next to the backup as
verify.json. It also reports drift, meaning any difference between
production's schema and what supabase/migrations/ builds.
Run it on demand: GitHub → Actions → Database backup → Run workflow.
See what you have: node --env-file=.env.local scripts/backup/fetch-backup.mjs --list.
Not in the backup: uploaded files (they're in R2, not the database), and Supabase dashboard settings (auth providers, email templates, SMTP, URL configuration; see CLAUDE.md "Accounts & sign-in").
Restoring means loading the data into a new Supabase project and pointing
the app at it. You need the PostgreSQL 17 client tools (psql) and the
Supabase CLI.
node --env-file=.env.local scripts/backup/fetch-backup.mjs --date=2026-10-04 --out=./restore
Leave out --date to get the newest. You can also download the files
from Cloudflare (R2 → nimina-backups → weekly/<date>/).export NEW_DB_URL='postgresql://postgres.<new-ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres'
supabase link --project-ref <new-ref>
supabase db push
psql "$NEW_DB_URL" -v ON_ERROR_STOP=1 -c "do \$\$ declare t text; begin for t in select format('%I.%I', schemaname, tablename) from pg_tables where schemaname = 'public' loop execute 'truncate ' || t || ' cascade'; end loop; end \$\$;"
gunzip -c restore/data.sql.gz | PGOPTIONS='-c session_replication_role=replica' psql "$NEW_DB_URL" -v ON_ERROR_STOP=1 --quiet
ok:
node scripts/backup/check-restore.mjs --manifest=restore/manifest.json --db="$NEW_DB_URL"
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY and
SUPABASE_SERVICE_ROLE_KEY to the new project's values, both in
Vercel and in .env.local, then redeploy.SUPABASE_DB_URL (GitHub secret) to the new project.The weekly check runs steps 3–6 against a stand-in project, so a green run means this exact procedure works on that week's data.
nimina-backups (private, no public
URL). Then create an API token with Object Read & Write limited to
that one bucket. Don't reuse the app's token: the app should never be
able to read the backups.db.<ref>.supabase.co host is IPv6-only,
and GitHub's runners can't reach it.SUPABASE_DB_URL (the session pooler string)R2_ACCOUNT_IDR2_BACKUP_ACCESS_KEY_IDR2_BACKUP_SECRET_ACCESS_KEYFree Supabase projects pause after 7 days without activity. A daily Vercel
Cron (vercel.json → /api/cron/keepalive) runs one real query. It needs
CRON_SECRET set in Vercel (any long random string); Vercel sends it
automatically, and the route refuses every other caller. The weekly backup
is a second, independent source of activity.