尚未检测到更新。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.