diff --git a/app/models/schemas/recipe.py b/app/models/schemas/recipe.py index f3c1640..139f383 100644 --- a/app/models/schemas/recipe.py +++ b/app/models/schemas/recipe.py @@ -83,6 +83,7 @@ class RecipeRequest(BaseModel): nutrition_filters: NutritionFilters = Field(default_factory=NutritionFilters) excluded_ids: list[int] = Field(default_factory=list) shopping_mode: bool = False + pantry_match_only: bool = False # when True, only return recipes with zero missing ingredients unit_system: str = "metric" # "metric" | "imperial" diff --git a/app/services/recipe/recipe_engine.py b/app/services/recipe/recipe_engine.py index 907d2be..46a42d3 100644 --- a/app/services/recipe/recipe_engine.py +++ b/app/services/recipe/recipe_engine.py @@ -699,6 +699,11 @@ class RecipeEngine: if not req.shopping_mode and effective_max_missing is not None and len(missing) > effective_max_missing: continue + # "Can make now" toggle: drop any recipe that still has missing ingredients + # after swaps are applied. Swapped items count as covered. + if req.pantry_match_only and missing: + continue + # L1 match ratio gate: drop results where less than 60% of the recipe's # ingredients are in the pantry. Prevents low-signal results like a # 10-ingredient recipe matching on only one common item. diff --git a/frontend/src/components/RecipesView.vue b/frontend/src/components/RecipesView.vue index efd97c7..0fa715b 100644 --- a/frontend/src/components/RecipesView.vue +++ b/frontend/src/components/RecipesView.vue @@ -169,6 +169,17 @@ No recipes containing these ingredients will appear. + +
+ Only recipes where every ingredient is in your pantry — no substitutions, no shopping. +
+