Serein
Rust로 만든 가볍고 성능 좋은 Discord 전용 네이티브 클라이언트
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Next.js app deployed to Vercel
README 전체를 불러오는 중입니다.
README 전문 보기 ↗This is a Next.js project bootstrapped with create-next-app.
Live: https://hello-world-nextjs-eight-blue.vercel.app
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying app/page.tsx. The page auto-updates as you edit the file.
This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.
To learn more about Next.js, take a look at the following resources:
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
This app gates /profile behind Supabase Auth using Google as the OAuth provider. To wire it up on a fresh Supabase project:
supabase/migrations/20260928000000_profiles.sql. It creates the profiles table, a trigger that inserts a row for every new auth.users signup, and an avatars storage bucket with policies scoping uploads to each user's own folder.https://<your-project-ref>.supabase.co/auth/v1/callback
http://localhost:3000/auth/callback
https://<your-vercel-app>/auth/callback
SUPABASE_URL, SUPABASE_ANON_KEY) are already server-side only — no changes needed there.New users are redirected to /profile after their first sign-in and prompted to fill in their name if it's missing. Signed-out visitors hitting /profile are redirected to /login.
Can Columbia students be funnier than AI? Signed-in users upload a photo, pick a caption voice, and Gemini writes four captions. Anyone signed in can then add their own caption to any photo. Human and AI captions are shown together with the author hidden; vote on one and it reveals whether a classmate or Gemini wrote it. A running scoreboard tracks which side wins more photos.
/): an editorial hero with the latest posts fanned out like polaroids, the Humans vs. Gemini scoreboard, today's challenge, and the feed as a masonry of battle cards or a photo wall (newest or top this week)./create): photo (upload with drag-and-drop, or pick a library photo such as today's drop), voice, and context, with a live preview and a step-by-step "the AI is cooking" sequence. Lands on the new post with a prompt to beat Gemini./challenge): a new prompt every midnight New York time, plus a featured photo when an admin has scheduled one, live photo and caption counts, the leaderboard, and yesterday's winner with its author revealed./me): your "era" (from the voice you use most), achievements computed only from what you've actually done, your photos with every caption on them, and your own captions with score and rank.battle_result(), never while voting is open)./surprise): two modes. Fresh photo rolls an unclaimed photo from the admin-curated library and sends it to the Studio. Random battle rolls a random photo already posted in the app and opens its battle, so you can jump in and caption without a photo of your own. Nothing is copied or generated; the photo stays its original owner's, and your caption is attributed to you. "Try another" re-rolls, avoiding the last few photos you've seen and your own.ENABLE_AI_PHOTOS=true to show a "Generate with AI" option on Post, where Gemini draws a photo from a description and then captions it (5 a day per user). Needs a Gemini API project with billing enabled; the free tier has no image quota./g/<id> link with a link-preview image.Setup:
supabase/migrations/20261005000000_captions_votes_rls.sql in the Supabase SQL Editor. It creates generations (photo + the exact prompt and model used), captions, and caption_votes (one row per user per caption, with a trigger that keeps the tallies on captions), the generations storage bucket, and turns on RLS for every table in public.supabase/migrations/20261012000000_human_vs_ai_battles.sql. It adds captions.author_type (existing captions become ai) and generations.challenge_date, hides caption authorship from the API, and adds the functions below.GEMINI_API_KEY in .env.local and in Vercel (Settings → Environment Variables). Optionally set GEMINI_MODEL (defaults to gemini-flash-latest) and GEMINI_IMAGE_MODEL (defaults to gemini-2.5-flash-image).RLS summary:
| Table | Read | Write |
|---|---|---|
profiles | own row | update own row (insert via signup trigger) |
movies | everyone | none |
generations | everyone | insert as yourself, into today's challenge only; update only context on your own posts (column grant); delete your own posts |
captions | everyone, but only id, generation_id, text, upvotes, downvotes, score (column grants) | AI captions: owner of a photo posted in the last 10 minutes. Human captions: only through submit_human_caption(). Tallies only via trigger |
caption_votes | own votes | insert / update / delete own votes; never on your own human caption |
Who wrote a caption (author_type, user_id) is never readable directly. These security definer functions decide what each caller may see:
| Function | Who | What |
|---|---|---|
caption_reveals(ids) | signed in | authors of captions you voted on, wrote, or that are on your photo |
submit_human_caption(id, text) | signed in | adds a human caption: 1–200 chars, max 3 per photo, 30 per day, no duplicates |
daily_winner(day) | everyone | top caption of a finished day, author revealed |
battle_scoreboard() | everyone | Humans vs. Gemini totals only |
my_captions() | signed in | your own human captions with rank |
edit_human_caption(id, text) | signed in | edit your own human caption, only while it has no votes |
delete_human_caption(id) | signed in | delete your own human caption |
guess_round(exclude) / check_guess(id, guess) | everyone | the guessing game; only uses captions from finished challenge days |
Captions come from the AI Studio pipeline (src/lib/pipeline.ts, shared with the admin app): the database picks a prompt chain by the admin-set mix, the app runs each step against Gemini with a per-call timeout and an overall time budget, and every call is logged (generation_requests, llm_responses). The tables and functions are in the admin repo's 20261026000000_ai_studio_access_images.sql. Before that migration is applied, the app uses the original single prompt.
Signups can be limited to approved email domains and whitelisted emails from the admin console. The login page explains the rule when it's on, and a blocked Google signup comes back with a clear message instead of a generic error.
Light-only brand system in src/app/globals.css: icy baby blue #B8DCFF, powder #E3F2FF, sky #69B5FF, navy ink #16263D on #F8FCFF. Headlines use Bricolage Grotesque with one Instrument Serif italic word; body text is Geist. Shared classes: .btn (glossy pills), .card, .glass, .chip, .field, .orb (the 3D bubble logo and Post button). Humans are blue #3D8FF0 and Gemini is bubblegum #E5539A; both were checked for color-blind separation and contrast. Motion respects prefers-reduced-motion.
npm test runs Playwright against a production build (npm run build first) and a local Supabase stand-in from the admin repo (morningside-memes-admin/tests/mock). The stand-in runs this repo's migrations with RLS on PGlite. The tests cover browsing signed out, the Human or AI? game, voting and the author reveal, adding/editing/deleting your own caption, deleting your own post, the profile form, the Daily Drop (and that future drops stay hidden), Surprise Me's fresh-photo mode, final battle results, the photo wall, Your Meme Era achievements, signup-gate messages, a pipeline failure being logged with no half-made post, posting with Gemini from an upload and from the Daily Drop (when GEMINI_API_KEY is set and has quota), and no horizontal scrolling on phones. They expect the admin repo checked out next to this one (or MOCK_DIR).
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.