尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A shared, phone-friendly grocery list for Amazon Fresh and Netcost Market.
/ is the shared page. Anyone with the link can leave a note on an item, add something, or remove an item with its × button. No sign-in. A removed item moves to a Removed card at the bottom of its store tab (with Put back), and is listed in the export as "Removed (don't order)" so Claude knows not to buy it. Removals last for the round only; the item itself isn't changed./edit is where the list is managed: edit items, import a new list, mark it final, copy it for Claude, start a new round./api/export is the read-only order that Claude reads to place the orders.There are no accounts. Anyone with the link can read and write, by design. Writes are rate limited per IP (30 a minute), and pages are marked noindex.
Next.js (App Router, TypeScript), plain CSS, and Upstash Redis from the Vercel Marketplace for storage. Runtime dependencies: next, react, react-dom, @upstash/redis.
npm install
npm run seed # first run only; loads data/seed.json
npm run dev # http://localhost:3000
Without Upstash credentials, local dev stores everything in .data/local-db.json (git-ignored). To use the real store locally instead:
vercel link
vercel env pull .env.local
npm run dev
vercel link then connect the repo under Project, Settings, Git). Pushes to main deploy production.KV_REST_API_URL and KV_REST_API_TOKEN on the project. UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN also work.vercel env pull .env.local && npm run seed. It only writes when the store has never been seeded and has no items. Running it again changes nothing.On Vercel, if the Redis env vars are missing, the API returns a clear "Storage isn't connected" error instead of silently using a file.
The list lives in Redis and changes only through /edit. data/seed.json is a first-run starting point only: the app never reads it, and deploys never reset the list.
On /edit you can:
Paste a JSON array of items:
[
{"store": "Amazon Fresh", "section": "Produce", "name": "Bananas"},
{"store": "Netcost", "section": "Deli", "name": "Doktorskaya", "size": "about 0.5 lb"}
]
store ("Amazon Fresh" or "Netcost"), section, name.id, size, price, imageUrl, sort, active (defaults to true).price is free text up to 40 characters, e.g. "$5.49", "$7.69/lb" or "$6.49/lb sale, reg. $7.69". The server records when each price was set (priceAt); you never send that.id when given, otherwise by store + name (case-insensitive). Matched items keep their id, their photo and price (unless a new imageUrl / price is given) and any note. To clear a price, edit the item on /edit. Unmatched entries become new items. Existing items not in the paste are removed, along with their notes.sort means the order in the pasted array.Entry 4 ("kefir"): same store and name as entry 3.GET /api/export returns JSON:
{
"generatedAt": "2026-10-25T18:10:00.000Z",
"status": "final",
"finalizedAt": "2026-10-25T18:10:00.000Z",
"orders": [
{
"store": "Amazon Fresh",
"items": [{"store": "Amazon Fresh", "section": "Produce", "name": "Blueberries", "size": null, "price": "$4.99", "priceAt": "2026-10-25T21:50:00.000Z", "note": "get 2"}],
"adds": [{"store": "Amazon Fresh", "text": "Milk"}],
"removed": [{"store": "Amazon Fresh", "section": "Meat and fish", "name": "Steak", "size": "about 1.5 lb"}]
},
{"store": "Netcost", "items": [], "adds": [], "removed": []},
{"store": "Either", "items": [], "adds": [{"store": "Either", "text": "Paper towels"}], "removed": []}
]
}
GET /api/export?format=text returns text/plain; charset=utf-8:
Grocery order (status: final, Oct 25, 2026 2:10 PM)
AMAZON FRESH
Produce
- Bananas
- Blueberries ($4.99; note: get 2)
Meat and fish
- Chicken thighs, boneless skinless, about 6 lb (2 packs)
Added
- Milk
Removed (don't order)
- Steak, about 1.5 lb
NETCOST
...
EITHER STORE
- Paper towels
;. Adds go under "Added" in their store; "Either" adds get their own block. Items taken off with × go under "Removed (don't order)" in their store. A store with nothing on it is left out.(status: draft).Cache-Control: no-store, Access-Control-Allow-Origin: *.One hash field per record, so concurrent writes to different items, notes or adds never overwrite each other:
| Key | Type | Contents |
|---|---|---|
g:items | hash | item id → {id, store, section, name, size?, imageUrl?, price?, priceAt?, active, sort} |
g:notes | hash | item id → {text, updatedAt} |
g:adds | hash | add id (client UUID) → {id, store, text, createdAt} |
g:meta | hash | status, finalizedAt, seededAt |
g:removed | hash | item id → {removedAt}: taken off this round's order |
g:rounds | list | newest first: {id, date, notes[{store,item,text}], adds[{store,text}], removed[{store,item}]} |
g:rl:<ip>:<minute> | counter | write rate limit, expires after 70s |
g:items in one MULTI/EXEC transaction, guarded by a fingerprint taken at preview./api routes are force-dynamic and send no-store.price (free text). It shows under the item on both pages in gray italics, under a "Prices are estimates." note, with its "as of" date on /edit, and in the export./edit. It adds the first dollar amount of each item's price; /lb prices are multiplied by the item's listed weight (1 lb if none). Removed items and free-text adds aren't counted./edit, or include price in the JSON you paste into Import.