Dough Planner Plus is a local-first baking planner for bread recipes.
It covers the full path from rough recipe intake to a structured baking workflow:
- store and search recipes locally
- edit ingredients and timed baking steps
- import recipes from pasted text or URLs
- review and refine imported drafts before saving
- generate a backward-planned timetable from a target ready time
- recipe library with search and quick actions
- recipe editor for ingredients, baker's percentages, tags, and timed steps
- backward-planned baking timetable based on the target ready date and time
- recipe import with two input modes: pasted text and recipe URLs
- AI-assisted structured extraction using the OpenAI Responses API through the Vercel AI SDK
- JSON-LD recipe extraction for sites that publish
schema.org/Recipedata - quick local parse fallback for offline-friendly or failure-tolerant text import
- editable import preview for recipe name, description, tags, ingredients, and steps
- local-first persistence in browser
localStorage - responsive React UI built with shadcn/ui and Tailwind CSS
- installable PWA support
- Vite
- React 18
- TypeScript
- Tailwind CSS
- shadcn/ui
- React Router
- TanStack Query
- Vercel AI SDK (
ai,@ai-sdk/openai) - Zod
- Vitest
- Node.js
- npm
OPENAI_API_KEYfor AI import
git clone <YOUR_GITHUB_REPO_URL>
cd dough-planner-plus
npm install
cp .env.example .envSet OPENAI_API_KEY in .env. The optional OPENAI_RECIPE_IMPORT_MODEL variable selects the OpenAI model used for recipe extraction and defaults to gpt-5-mini.
npm run devStarts the Vite development server on port 8080 by default. The /api/recipe-import endpoint is served in development through a Vite middleware plugin.
npm run buildBuilds the production client bundle.
npm run build:devBuilds the client bundle using the development mode Vite configuration.
npm run previewServes the production build locally.
npm run testRuns the Vitest suite once.
npm run test:watchRuns Vitest in watch mode.
npm run lintRuns ESLint across the repository.
The repository includes .env.example:
OPENAI_API_KEY=your_openai_api_key
OPENAI_RECIPE_IMPORT_MODEL=gpt-5-miniOPENAI_API_KEY is read only on the server-side import path. The browser never calls OpenAI directly.
Recipe import is available at /import and supports two modes:
Text: paste recipe text and chooseImport with AIorQuick ParseURL: provide a recipe page URL and import it through the server route
The server import contract is:
POST /api/recipe-import
Request body:
{ "mode": "text", "text": "..." }or
{ "mode": "url", "url": "https://example.com/recipe" }Response body:
{
"recipe": {
"name": "Pan Bread",
"description": "",
"tags": ["Flatbread"],
"ingredients": [{ "name": "Flour", "amount": "500", "unit": "g" }],
"steps": [{ "name": "Bake", "durationMinutes": 25, "instructions": "Bake until golden." }],
"warnings": []
},
"source": "jsonld",
"warnings": []
}Import precedence:
- validate the request
- for URL imports, fetch the recipe page server-side
- extract
schema.org/RecipeJSON-LD when available - fall back to AI extraction with a schema-constrained response
- fall back to quick local parse when AI extraction is unavailable or unusable
- present the normalized draft for editing and save it locally
The recipe-parsing prompt lives in server/lib/parse-recipe-with-ai.ts.
The main prompt constant is:
RECIPE_IMPORT_SYSTEM_PROMPT
That is the place to edit the extraction instructions when you want to change how the AI handles:
- what belongs in
description - what belongs in
notes - ingredient parsing behavior
- step naming and instruction style
- how conservative or aggressive the extraction should be
Related tuning points:
OPENAI_RECIPE_IMPORT_MODELin.envselects the model used for parsing- src/lib/import/import-schema.ts defines the structured output shape the model must return
- src/lib/import/normalize-import.ts defines the cleanup and post-processing rules applied after generation
If you want different parsing behavior, update the prompt first, then adjust the schema or normalization rules only when the data contract itself needs to change.
Recipes are stored in browser localStorage under the bread-planner-recipes key.
That means:
- saved recipes stay on the device unless exported manually
- no backend database is required for recipe storage
- clearing browser storage removes saved recipes
The AI import pipeline uses a small server boundary for URL fetching and OpenAI calls, while the saved recipe library remains local-first.
api/
recipe-import.ts Production server entrypoint for recipe import
server/
lib/ Server-side import pipeline helpers
src/
components/ UI building blocks
lib/import/ Shared import schema, normalization, and quick parse helpers
lib/ Storage and client utilities
pages/ Route-level screens
test/import/ Import-focused tests
test/ Test setup and general tests
types/ Shared TypeScript types
docs/
ai-recipe-import-plan.md Import architecture and implementation reference
prompt.md Product brief and scope
screenshots/ README screenshots
Development uses the Vite middleware plugin in server/vite-recipe-import-plugin.ts to expose /api/recipe-import.
Production expects a runtime that can serve api/recipe-import.ts as an HTTP endpoint and provide OPENAI_API_KEY in the server environment.
The repository includes targeted tests for:
- quick parse behavior
- import normalization
- JSON-LD recipe extraction
- general test setup
Run all tests with npm run test.
Dough Planner Plus is a focused bread-planning application:
- no user accounts
- no backend recipe database
- no cloud sync
The primary workflow is local recipe planning, with a server-side import boundary for AI-assisted recipe structuring.



