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. +

+
+
- -
+ +
{ const category = ref(null) const wildcardConfirmed = ref(false) const shoppingMode = ref(false) + const pantryMatchOnly = ref(false) const nutritionFilters = ref({ max_calories: null, max_sugar_g: null, @@ -176,6 +177,7 @@ export const useRecipesStore = defineStore('recipes', () => { nutrition_filters: nutritionFilters.value, excluded_ids: [...excluded], shopping_mode: shoppingMode.value, + pantry_match_only: pantryMatchOnly.value, } } @@ -306,6 +308,7 @@ export const useRecipesStore = defineStore('recipes', () => { category, wildcardConfirmed, shoppingMode, + pantryMatchOnly, nutritionFilters, dismissedIds, dismissedCount,