尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
A one-screen web app for a family. For each holiday it asks: ** איפה אתם בחג? ** — אני מארח , or מתארח אצל + a family picked from a dropdown.檢視完整 README ↗
A one-screen web app for a family. For each holiday it asks:
איפה אתם בחג? — אני מארח, or מתארח אצל + a family picked from a dropdown.
Sign in with a phone number and a name — no password, no email, no code, and no invite needed — remembered in a browser cookie so you only ever type it once. A number the sheet already knows is not let in by being typed, though: from a new device it needs a link somebody in the family aimed at it, so nobody can become a relative by knowing their number (see docs/GOING-LIVE.md). You start with nobody on your list, and every family you add brings the families it knows along as suggestions. A Google Sheet is the entire database, and everything is managed by hand in it. Hebrew, right-to-left.
Three screens on a tab bar — the question, המעגלים (your families, and invites), and היסטוריה (what happened, and correcting it) — plus המועדים שלנו, reached from the household menu at the top. An occasion is a date this family gathers on and others don't; it goes out to your circle unless you narrow it, and only the families it reaches are asked about it.
The household name sits at the top of every screen and opens a small menu: your occasions, a plain share of the app for a friend who just likes the idea — no invite, no connection — and the way out.
Answering steps through the whole year: swipe sideways, or use the arrows, to reach any holiday from the next one up to that same holiday a year later.
See docs/PLAN.md for the design, docs/GROWTH.md for how it reaches families beyond your own, and docs/OPEN-QUESTIONS.md for what's still undecided.
With no SHEET_ID set the app reads and writes .dev-sheet.json instead of a real sheet, so you
can see the whole thing working before creating anything in Google.
npm install
npm run seed:holidays # fills the Holidays tab with candidate dates
npm run fixtures # sample families, so the dropdown isn't empty
Then open .dev-sheet.json, find a holiday in the Holidays tab and set its include column to
TRUE — that's the same edit you'll make in the real sheet. Now:
SESSION_SECRET=dev npm run dev
Sign in with any phone number. The first one you use is unknown, so it will ask for your name and which family you are.
To run the end-to-end check (needs a production build running on port 3111):
npm run build
npm run start:test &
npm run test:smoke
start:test clears SHEET_ID on purpose: next start reads .env.local, so a
server started the ordinary way runs against the real spreadsheet, and the suite
— which registers families and answers for holidays — would write into it. The
suite checks which store the server is on before it does anything, and stops if
it is not the local file.
Sign-in is a phone number and nothing else, so trying a scenario end to end means being several people — and doing it against the real record would put invented families into it. Both are config, not code:
A scratch sheet, and a second deployment. Make a second Google Sheet, share it
with the same service account, then run npm run setup and npm run seed:holidays
against it. On Vercel, import the repo a second time as its own project with
SHEET_ID pointed at that sheet and PLAYGROUND=1 — see
docs/GOING-LIVE.md. Both projects track the same branch, so
one push updates both. The scratch URL carries a standing red strip —
סביבת ניסיון — so the two can never be confused, and being a
different origin it keeps its own cookie.
Several people at once, on one phone. Every browser has its own cookie jar, so
Chrome, a Chrome incognito tab, Safari and Firefox are four households side by
side. Within one browser, יציאה on the families screen switches who you are.
Numbers are never verified, so 050-000-0001 and friends work as well as real ones.
The real record, to try things against. npm run copy-sheet says what it
would copy from the sheet in .env.local into the scratch sheet; npm run copy-sheet -- --apply does it. One way only — the source is read and never
written — and the scratch sheet's contents are dumped to a .sheet-backup-*.json
first. --from and --to point it somewhere else.
Empty, to start over. npm run clear-sheet says what it would empty in the
scratch sheet; npm run clear-sheet -- --apply does it, leaving every tab with
its header row and nothing else. The holidays are kept — they are the calendar,
not anybody's record — unless --holidays says otherwise. It refuses to touch
the sheet in SHEET_ID.
Back to the start. PLAYGROUND=1 npm run reset restores the scratch sheet to a
fixed cast — a family with two people, one with a single person, one nobody has
joined, and a suggestion waiting to be taken up — and prints the numbers to sign in
with. It refuses to run without PLAYGROUND=1, so it cannot empty the real sheet.
Run npm run setup and the tabs are created for you: Holidays, Households, People,
Answers, Conflicts, Connections, Invites. Only the first two need anything typed into them.
Households — the families.
| household_id | name | active |
|---|---|---|
| hh_parents | אבא ואמא | TRUE |
People — which number belongs to which family. The app appends here when somebody registers, and
you can add rows yourself.
| phone | name | household_id |
|---|---|---|
| +972501234567 | אבא | hh_parents |
Columns are matched by header name, so you can reorder or add columns without breaking anything.
Family members never get access to the sheet — only the robot account and you.
Copy .env.example to .env.local and fill in SHEET_ID, the two service-account values, and any
long random string for SESSION_SECRET. Then:
npm run seed:holidays -- --years 10
This writes every candidate date — חג, ערב חג,
מועד — into the Holidays tab with include = FALSE. Go through the tab
and set TRUE on the dates your family actually gathers for. That decision lives in the
spreadsheet; the app has no calendar logic of its own.
Re-running the seed is safe: rows you've edited keep their include value, and rows you added by
hand are left alone. Add --dry to see what it would do without writing.
Any Node host works. On Vercel, set the same four environment variables in the project settings and deploy — nothing else is needed, because there is nothing else to run.
Every tab is append-only: an answer, a connection, an invite, a conflict, a family's own occasion. Nothing is ever edited or deleted in place — correcting the record means a newer row, and the newest row for a key wins. That is what makes two people answering at the same moment safe, and it means the sheet doubles as its own history.
The one exception is npm run align, a maintenance command you run by hand: it adds any newly
introduced column name to a tab's header row, leaving every data row untouched.
src/app/ the four screens (question, families, history, occasions) and the server actions
src/lib/ sheet access, the domain layer, phone normalization, the session cookie
src/components/ the forms and the tab bar
scripts/ setup, the holiday seeder, header alignment, and local dev fixtures
tests/smoke.mjs end-to-end check of every flow above