No description
  • TypeScript 89.3%
  • JavaScript 8.5%
  • CSS 1.9%
  • Shell 0.2%
Find a file
2026-05-23 17:40:20 +02:00
public Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
scripts Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
src Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
.env.local.example Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
.gitignore Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
eslint.config.js Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
HANDOFF.md Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
index.html Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
package-lock.json Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
package.json Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
postcss.config.js Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
README.md Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
tailwind.config.js Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
tsconfig.app.json Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
tsconfig.json Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
tsconfig.node.json Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00
vite.config.ts Initial commit: Ember recipe book app 2026-05-23 17:40:20 +02:00

Ember

A personal vegetarian dinner app with a transparent taste-learning loop. Built for one user — you.

What's in this build (tier 0+)

The learning loop:

  • Home screen — top match, three different directions, "Because you cooked X," "Crispy & substantial," Learning Corner.
  • Recipe Detail — Why-this panel showing live signal weights, source attribution, ingredients, method, texture tip, failure warning. Plus/minus stepper next to "Serves" scales all ingredient quantities live (with smart rounding — eggs stay whole, grams round to 5/10/25).
  • Taste Profile — editable weights, pattern insights, novelty slider, reset.
  • Post-cook feedback modal — 3-button verdict + reason chips → updates weights and shows the receipt.

The Tuesday-at-6pm tool:

  • Cook from Ingredients (rule-based, no API) — pick ingredients (autocomplete from your library + pantry), optional filters (time / vibe / texture), star one as must-use. Returns 5 format-diverse matches from your library ranked by ingredient overlap × taste score. Each shows match %, missing ingredients, source attribution.

The library:

  • 20 seeded recipes with diverse authors and visual styles — inspired by Ottolenghi, Hetty McKinnon, Felicity Cloake, J. Kenji López-Alt, Andrea Nguyen, Smitten Kitchen, Eric Kim, Pailin Chongchitnant, Sohla El-Waylly, Anna Jones, Madhur Jaffrey.
  • Recipes tab — search, filter by cuisine, browse All / Saved / Cooked / Imported.
  • Source attribution on every recipe — type (house / imported / inspired), author, publication, optional URL, optional inspiration note.

The growth path:

  • URL import — paste a URL from any site that publishes schema.org/Recipe data (NYT Cooking, Bon Appétit, Smitten Kitchen, Serious Eats, Guardian, BBC Good Food, Half Baked Harvest, etc.). Goes through a CORS proxy, auto-tags with sensible defaults, you adjust before saving.
  • Paste-HTML fallback — when the URL fetch is blocked (CORS / paywall / wifi), paste the page HTML directly.

Persistence:

  • localStorage key ember.userState.v1 holds your weights, interactions, saved/cooked sets, novelty ratio, and imported recipes.
  • No backend, no accounts, no third-party analytics.

What's not in this build (still deferred)

  • Weekly Planner · Shopping List · Cook Mode · Technique Lab.
  • "Goes well with" pairings on Recipe Detail (next obvious add).
  • LLM-assisted parsing for messy sites without schema.org/Recipe.
  • RSS-feed-based discovery. Requires a server — that's tier 2.
  • Vercel deploy (5-minute job when you want it).
  • AI recipe generation. Deliberately skipped — rule-based matching against your real library is honest and doesn't need an API key.

Run it

npm install
npm run dev -- --host 0.0.0.0

The dev server prints two URLs:

  • Local — open on this machine: http://localhost:5173
  • Network — open on your phone (same wifi): e.g. http://192.168.x.x:5173

Open on your phone (the real test)

  1. Make sure your phone is on the same wifi as this Mac.
  2. Open Safari, type the Network URL.
  3. Tap the share icon → Add to Home Screen. Installs like a real app.

If the network URL doesn't work: AP isolation is enabled on the wifi. Switch it off in router settings, or run npx ngrok http 5173 for a temporary public URL.

The recipe sourcing strategy (the long view)

Stage What it is Where you are now
Manual seeds Hand-written house recipes with attribution to the chefs they emulate. ✓ Built (20 recipes)
URL import Paste any URL with schema.org Recipe data → app parses, you adjust tags, save. ✓ Built
Paste HTML fallback For sites blocked by CORS — paste page source manually. ✓ Built
LLM-assisted parsing For messy pages with no schema, send to Claude API for parsing. Tier 1
RSS discovery feed Subscribe to NYT Cooking, Guardian, Smitten Kitchen, Ottolenghi, etc. Daily cron fetches new posts, Claude pre-tags, you adopt the keepers. Tier 2 (needs server)
Self-hosted on your server Move from localStorage to your own DB, enable RSS cron jobs, optional sync between devices. Tier 2+

Mass-scraping entire sites is deliberately not on this list — both legally murky and a maintenance nightmare. RSS + structured-data extraction is the clean path.

How the learning loop works

Every interaction adjusts weights immediately:

Action Effect
Open a recipe Logged. No weight change. (Implicit signal — future use.)
✕ Skip (Recipe Detail) 0.08 across tags. 365-day cooldown on this recipe.
☆ Save Marks saved (implicit positive).
🍳 Cook this Marks as cooked, opens the post-cook modal.
Post-cook: Make again +0.12 across tags. Strongest positive signal.
Post-cook: It was fine +0.02. Mild positive.
Post-cook: Not worth it 0.15. Strong negative.
Reason chips (positive) +0.04 with category-specific bumps (crispy → texture weight).
Reason chips (negative) 0.05 with anti-flag-specific bumps (mushy → mushy anti-tag).

The Why-this panel and Taste Profile read directly from these weights. No hidden model — what you see is what's scoring.

Source attribution model

Every recipe carries:

source: {
  type: 'house' | 'imported' | 'inspired',
  url?: string,          // present for imported
  author?: string,       // chef / writer
  publication?: string,  // NYT Cooking, Smitten Kitchen, etc.
  note?: string,         // free-text inspiration note
}
  • house — composed by Ember, with a note pointing to the chef/style that inspired the technique.
  • imported — parsed from a URL via the import flow, full attribution preserved, original URL stored for verification.
  • inspired — composed but explicitly modelled on a specific named recipe.

Every Recipe Detail shows a source panel at the top. Every mini card on Home and Recipes shows a small attribution line.

Add or change recipes manually

Edit src/data/recipes.ts. The Recipe shape:

{
  id, title, subtitle,
  source: { type, author, publication, note, url? },
  cuisine, format, textures[], carbBase, vibes[], antiTags[],
  keyIngredients[], timeMinutes, effort, serves,
  ingredients: [{ qty, name }],
  steps: [string],
  textureTip,         // the one technique move that matters
  failureWarning,     // what usually goes wrong
  imageClass,         // CSS gradient class in src/index.css
  recoveryAdaptation? // optional cycling-mode swap
}

Adding more recipes shows up on Home immediately — the scoring engine doesn't need a rebuild.

Reset your taste profile

Taste Profile screen → "Reset everything" at the bottom. Wipes localStorage and reloads seed weights.

Stack

  • Vite + React 18 + TypeScript
  • Tailwind CSS v3 (warm palette: ivory, ember, olive, paprika, brick — no purple AI gradients)
  • localStorage for persistence
  • Recipe parsing: schema.org/Recipe JSON-LD via DOM regex + CORS proxy (corsproxy.io with AllOrigins fallback)

Next session candidates

  1. LLM-assisted parsing when schema.org is missing or broken — send page text to Claude API, get a structured Recipe back. Needs an API key.
  2. Cook from Ingredients — five-direction generator from your pantry.
  3. Weekly Planner + Shopping List.
  4. Move to a real server — your own host, Postgres, RSS cron jobs for nightly discovery. The big one.
  5. Deploy to Vercel (interim) so you can use it on the road.