iArt.ai
AI motion graphics software — the After Effects alternative anyone can use. Create animations and explainer videos from a text prompt in minutes.
A record of what you've watched and what you made of it. A list of your own, one you share, and the full catalogue to look through.
MediaTypeScript · PLpgSQLGitHub @tamkin-anwarOnlineOwner 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
A watch tracker you keep with the people you watch with: a personal list, a shared list for two, and a live library of what to watch. React, Supabase, TMDB.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A watch tracker you keep with the people you watch with. Every film and series you have seen or mean to, with your own rating and notes, plus a second list you share with a friend (or a few) where everyone adds titles and rates them. Real posters, cast, and runtimes come from TMDB; accounts and data live on Supabase.
Web first (React), with the backend kept as a plain API so a native iOS client can reuse it later. Every user's data is isolated at the database level, so this runs as a real multi-user app, not a single-owner tool.
Built by Anwar Creative Studio.Read the full README ↗A watch tracker you keep with the people you watch with. Every film and series you have seen or mean to, with your own rating and notes, plus a second list you share with a friend (or a few) where everyone adds titles and rates them. Real posters, cast, and runtimes come from TMDB; accounts and data live on Supabase.
Web first (React), with the backend kept as a plain API so a native iOS client can reuse it later. Every user's data is isolated at the database level, so this runs as a real multi-user app, not a single-owner tool.
Built by Anwar Creative Studio.
A list entry has an owner_type of either user or space. A personal list and a shared list run through the same code, they just point at a different owner. Ratings are their own table, one row per person per entry, which is why a shared entry can hold two ratings at once.
Titles are cached in a local titles table when first added, so a list points at a stable row and your history does not shift if TMDB changes.
Postgres row level security does the access control. A signed-in user can read and write their own user entries and any space entry for a space they belong to. Ratings are readable by anyone who can see the parent entry and writable only for your own row. Neither list_entries nor ratings nor the RLS policies that gate them cap a space's membership; the two security definer functions that grow one do that instead. Starting a space (create_couple_space) and adding another member to an existing one (add_space_member) both first check the friendship being relied on is accepted — and specifically the acting user's own friendship with whoever is being added, not any existing member's.
/api/* Vercel Edge functions proxy TMDB, OMDb and The Guardian: the keys stay server-side and responses are cached on the CDN, so the free-tier limits are shared across everyone rather than spent per visitThe browser never talks to TMDB, OMDb or The Guardian directly. It calls
/api/tmdb, /api/omdb and /api/guardian, which:
VITE_ prefix, never bundled),Cache-Control: s-maxage so Vercel's edge caches the response — title
detail for a day, discovery feeds for 15 minutes, the press feed for 30,
IMDb/RT scores for a week,So the first person to open "trending" this hour pays the upstream calls; the
next few thousand are served from the edge. During vite dev a small plugin
in vite.config.ts runs the same handler modules so /api/* works locally.
Each function also runs a best-effort per-IP rate limit (in the edge instance's memory, ceilings well above any real session) so one client cannot run the free-tier budgets down. For a hard limit, add a rule in the Vercel firewall.
/api/poster is opt-in (see the env table). When it and VITE_POSTER_CACHE
are set, it copies each TMDB poster into a public Supabase Storage bucket the
first time a card renders it, and the grid serves the Storage copy from then
on. Without it, cards use TMDB image URLs as before.
Letterboxd is a public diary. A group text is where "what should we watch" goes to die. Stub is the small private middle: one list that is yours, one that is ours, and a real catalogue to pull from, with no feed and no account required from anyone you are not already watching with.
npm install
supabase/migrations/ in order (0001 through 0008), or supabase link and supabase db push.anon public key.eyJ).cp .env.example .env
# then fill in the values
| var | side | required | what |
|---|---|---|---|
VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY | client | yes | the database and auth |
TMDB_ACCESS_TOKEN | server (/api/*) | yes | titles, artwork, discovery |
OMDB_API_KEY | server | no | IMDb / Rotten Tomatoes / Metacritic scores |
GUARDIAN_API_KEY | server | no | the "In the press" feed |
SUPABASE_SERVICE_ROLE_KEY | server | no | lets /api/poster write to Storage |
VITE_POSTER_CACHE | client | no | set to 1 to serve posters from Storage |
VITE_SENTRY_DSN | client | no | a Sentry DSN turns on crash reporting |
The server vars have no VITE_ prefix on purpose, so they never reach the
browser. On Vercel, add them as plain (not VITE_) environment variables;
vite dev reads them from .env for the local /api plugin. For the poster
cache, also run supabase/migrations/0005_posters_bucket.sql and set
VITE_POSTER_CACHE=1.
npm run dev
Open the local URL Vite prints, create an account, and start adding titles from the Library. Until the two Supabase values are set, the app shows a setup screen instead of booting.
| Table | Purpose |
|---|---|
profiles | One row per auth user, created by a trigger on sign up. Holds the username and display name. |
titles | Local cache of TMDB media so lists reference a stable row. |
list_entries | A title on a list. owner_type is user or space; carries status, note, and watched date. |
ratings | One row per person per entry. This is how everyone's ratings on a shared entry sit side by side. |
friendships | Requester, addressee, and status (pending or accepted). |
spaces + space_members | A shared list and who belongs to it. create_couple_space(friend) starts one between two accepted friends; add_space_member(space, friend) grows an existing one, up to 8 people, gated on the adder's own friendship with whoever they're adding. |
notifications | One row per (recipient, entry, kind). Triggers on ratings and list_entries.note write these for the other space member; a repeat rating or note edit refreshes the row instead of adding another. Delivered live over Supabase realtime. |
npm run dev # dev server
npm run build # typecheck, then production build
npm run typecheck # types only
npm test # unit tests (Vitest)
npm run test:db # row-level-security tests (needs a local Supabase: supabase start)
npm run preview # serve the build
npm run icons # regenerate the PNG icons from the mark
npm test covers the pure logic: OMDb score parsing, Guardian article mapping, TMDB normalisation and the upcoming/classic filters, list averages, and theme persistence.
supabase/tests/rls_test.sql is a pgTAP suite that runs against a throwaway database (supabase start && npm run test:db, or the CI database job). It asserts that one user cannot read or write another's personal list or ratings or profile, that a shared space is visible only to its members, that a space can grow past a pair but only when the acting member has their own accepted friendship with whoever they're adding (not merely riding on a fellow member's), that friend_activity and list_compare only return data between accepted friends, and that delete_own_account removes exactly the caller and their personal rows.
CI (.github/workflows/ci.yml) runs typecheck, unit tests and the build on every push, plus the database tests on a fresh Supabase stack.
/api/* proxy that keeps the third-party keys server-side and caches their responses on the CDN./api/poster caches TMDB poster art into Supabase Storage so the grid does not depend on TMDB's CDN. Off until SUPABASE_SERVICE_ROLE_KEY and VITE_POSTER_CACHE are set./api/* function, and crash reporting to Sentry when VITE_SENTRY_DSN is set.prototype/archive.html is the original single file version that seeded the idea: one HTML file, no accounts, list kept in the page itself.