Serein
Rust로 만든 가볍고 성능 좋은 Discord 전용 네이티브 클라이언트
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A shared camera for friend groups: call roll and everyone shoots the same moment, with short challenges, and it all develops the next day.
CLAUDE.md: the build brief. Start here.prototype/index.html: the working prototype (design, rules, photo look, sample event). Serve the folder over HTTP to try it (npx serve prototype); the sample photos won't process from a file:// URL. Outside claude.ai the prototype has no database, so creating groups is disabled; the sample event and "Try the look" still work.docs/challenges.md: every challenge.docs/recreate-challenges.xlsx: recreations with copyright notes.docs/research.md: the research behind the decisions.docs/credits.md: photo and font credits.Next.js (App Router) and Supabase. Node 20 or later.
npm install
cp .env.example .env.local # then fill in the two values
npm run dev
Needs Docker. npx supabase start applies supabase/migrations, uses the auth settings in supabase/config.toml and prints the API URL and publishable key for .env.local. Sign-in emails land in Inbucket at http://127.0.0.1:54324.
npx supabase link --project-ref <ref>, then npx supabase db push. Or paste each file in supabase/migrations into the SQL Editor, oldest first.https://<your-domain>/** (plus Vercel preview URLs if you use them) to the redirect URLs.smtp.resend.com, port 465, user resend, password a Resend API key), raise the email rate limit, then paste supabase/templates/sign-in.html into both "Confirm signup" and "Magic link", and supabase/templates/email-change.html into "Change email address". The default templates work, but only send a link. Ours add a code, because a home-screen app on iPhone doesn't share cookies with Safari, so the link alone signs people in on the wrong side.npx web-push generate-vapid-keys): NEXT_PUBLIC_VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY (secret).supabase/migrations/20261021000000_foundations.sql has every table from the brief, plus a timezone on groups and host_member_id on events. Row-level security is on everywhere: people see only their groups, and other people's photos (rows and Storage objects) stay hidden until the reveal. Groups are made with create_group() and joined with join_group() from an invite link; group_preview() lets the invite page show the group before you sign in. Events are made with create_event(), which saves the event and its guest list together. event_shot_counts() gives the group each person's shot count without unsealing anything. Roll Calls go through call_roll() and answer_roll(), which apply the budget, reserved call, one-per-person rule, cooldown, backed extras and answer windows in one transaction. roll_state() returns the same check for the button, so the app and the server always agree. ask_extra() and back_extra() handle asking for one more. Roll calls, asks, backs and statuses are published to Realtime. Push: devices register with save_push_subscription(); after a call, /api/push/roll-call gets the recipients once through roll_call_push_targets() (caller only, under a minute old, snoozed and home left out) and sends with web-push.
Profiles (20261027000000_profile.sql): one name and one away card per person. set_my_name() and set_my_away_card() update every group you're in; making or joining a group copies them in. Profile cards are stored at away-cards/u/{user_id}/.
Start-day alerts (20261031000000_start_push.sql): "Sam's 30th is today", once, from 09:00 local on the first day, sent the same three ways as reveal alerts.
Reveal alerts (20261030000000_reveal_push.sql): "Lisbon is out" goes to everyone on an event once it reveals. Whichever comes first sends it, once: the host pressing Reveal now, anyone in the group opening the app after the reveal time, or the daily Vercel cron in vercel.json (needs CRON_SECRET in Vercel and the same value in private.secrets).
Letter-day word (20261029000000_hunt_word.sql): events.hunt_word, set by the host when creating a trip; event_details is rebuilt to include it.
Negatives (20261028000000_negatives.sql): at capture the phone also uploads a 45×60 inverted, tinted negative to the negatives bucket. The group can read those before the reveal, and event_negatives() lists which shots exist without saying what they are.
Moderation and cleanup (20261026000000_moderation.sql): report_photo() reports a revealed photo; the event's host and the group's maker see reports (reports table) and can remove any revealed photo, and owners can always delete their own. leave_group() deletes your photos and event places, keeps Roll Calls you started, hands events you host (and the group, if you made it) to whoever joined first, and leaves your name unclaimed so you can rejoin as yourself; the last person out deletes the group. Anyone in the group can join an event until its reveal; late joiners sort last so nobody else's challenge moves. Photo files are cleaned up by their uploader: Storage only deletes what you can read, so my_orphaned_files() lists your files that no row points at any more and the app removes them on load. Until then nobody else can read them, because reading a photo or away card now needs a live row.