Compare commits

...

5 Commits

Author SHA1 Message Date
Kuchenpirat
6e16a16cd8 Merge branch 'mealie-next' into reset-scroll-position 2025-07-12 01:32:32 +02:00
Kuchenpirat
78a0f74f33 reset scroll position 2025-07-11 23:29:09 +00:00
Hayden
8b9e80358b chore(l10n): New Crowdin updates (#5682) 2025-07-11 21:18:48 +00:00
github-actions[bot]
2bae6e9d02 docs(auto): Update image tag, for release v3.0.0 (#5675)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
2025-07-11 22:38:46 +02:00
renovate[bot]
6b98a7cd74 fix(deps): update dependency openai to v1.95.0 (#5671)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-11 19:22:03 +02:00
13 changed files with 321 additions and 150 deletions

View File

@@ -31,7 +31,7 @@ To deploy mealie on your local network, it is highly recommended to use Docker t
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do: We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do:
1. Take a backup just in case! 1. Take a backup just in case!
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v2.8.0` 2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.0.0`
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access. 3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
4. Restart the container 4. Restart the container

View File

@@ -7,7 +7,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
```yaml ```yaml
services: services:
mealie: mealie:
image: ghcr.io/mealie-recipes/mealie:v2.8.0 # (3) image: ghcr.io/mealie-recipes/mealie:v3.0.0 # (3)
container_name: mealie container_name: mealie
restart: always restart: always
ports: ports:

View File

@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
```yaml ```yaml
services: services:
mealie: mealie:
image: ghcr.io/mealie-recipes/mealie:v2.8.0 # (3) image: ghcr.io/mealie-recipes/mealie:v3.0.0 # (3)
container_name: mealie container_name: mealie
restart: always restart: always
ports: ports:

View File

@@ -123,6 +123,7 @@
:image="recipe.image!" :image="recipe.image!"
:tags="recipe.tags!" :tags="recipe.tags!"
:recipe-id="recipe.id!" :recipe-id="recipe.id!"
@click="handleRecipeNavigation"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -147,6 +148,7 @@
:image="recipe.image!" :image="recipe.image!"
:tags="recipe.tags!" :tags="recipe.tags!"
:recipe-id="recipe.id!" :recipe-id="recipe.id!"
@selected="handleRecipeNavigation"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -171,6 +173,7 @@ import { useLazyRecipes } from "~/composables/recipes";
import type { Recipe } from "~/lib/api/types/recipe"; import type { Recipe } from "~/lib/api/types/recipe";
import { useUserSortPreferences } from "~/composables/use-users/preferences"; import { useUserSortPreferences } from "~/composables/use-users/preferences";
import type { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe"; import type { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
import { useRecipeListState } from "~/composables/recipe-page/use-recipe-list-state";
const REPLACE_RECIPES_EVENT = "replaceRecipes"; const REPLACE_RECIPES_EVENT = "replaceRecipes";
const APPEND_RECIPES_EVENT = "appendRecipes"; const APPEND_RECIPES_EVENT = "appendRecipes";
@@ -241,9 +244,11 @@ export default defineNuxtComponent({
const route = useRoute(); const route = useRoute();
const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || "");
const page = ref(1); const recipeListState = useRecipeListState(props.query);
const page = ref(recipeListState.state.page || 1);
const perPage = 32; const perPage = 32;
const hasMore = ref(true); const hasMore = ref(recipeListState.state.hasMore);
const ready = ref(false); const ready = ref(false);
const loading = ref(false); const loading = ref(false);
@@ -282,8 +287,33 @@ export default defineNuxtComponent({
); );
} }
// Save scroll position
const throttledScrollSave = useThrottleFn(() => {
recipeListState.saveScrollPosition();
}, 1000);
onMounted(async () => { onMounted(async () => {
window.addEventListener("scroll", throttledScrollSave);
// cached state with scroll position
if (recipeListState.hasValidState() && recipeListState.isQueryMatch(props.query)) {
// Restore from cached state
page.value = recipeListState.state.page;
hasMore.value = recipeListState.state.hasMore;
ready.value = recipeListState.state.ready;
// Emit cached recipes
context.emit(REPLACE_RECIPES_EVENT, recipeListState.state.recipes);
// Restore scroll position after recipes are rendered
nextTick(() => {
recipeListState.restoreScrollPosition();
});
}
else {
// Initialize fresh recipes
await initRecipes(); await initRecipes();
}
ready.value = true; ready.value = true;
}); });
@@ -294,6 +324,10 @@ export default defineNuxtComponent({
const newValueString = JSON.stringify(newValue); const newValueString = JSON.stringify(newValue);
if (lastQuery !== newValueString) { if (lastQuery !== newValueString) {
lastQuery = newValueString; lastQuery = newValueString;
// Save scroll position before query change
recipeListState.saveScrollPosition();
ready.value = false; ready.value = false;
await initRecipes(); await initRecipes();
ready.value = true; ready.value = true;
@@ -315,6 +349,14 @@ export default defineNuxtComponent({
// since we doubled the first call, we also need to advance the page // since we doubled the first call, we also need to advance the page
page.value = page.value + 1; page.value = page.value + 1;
// Save state after fetching recipes
recipeListState.saveState({
recipes: newRecipes,
page: page.value,
hasMore: hasMore.value,
ready: true,
});
context.emit(REPLACE_RECIPES_EVENT, newRecipes); context.emit(REPLACE_RECIPES_EVENT, newRecipes);
} }
@@ -331,6 +373,14 @@ export default defineNuxtComponent({
hasMore.value = false; hasMore.value = false;
} }
if (newRecipes.length) { if (newRecipes.length) {
// Update cached state with new recipes
const allRecipes = [...(recipeListState.state.recipes || []), ...newRecipes] as Recipe[];
recipeListState.saveState({
recipes: allRecipes,
page: page.value,
hasMore: hasMore.value,
});
context.emit(APPEND_RECIPES_EVENT, newRecipes); context.emit(APPEND_RECIPES_EVENT, newRecipes);
} }
@@ -408,6 +458,15 @@ export default defineNuxtComponent({
// fetch new recipes // fetch new recipes
const newRecipes = await fetchRecipes(); const newRecipes = await fetchRecipes();
// Update cached state
recipeListState.saveState({
recipes: newRecipes,
page: page.value,
hasMore: hasMore.value,
ready: true,
});
context.emit(REPLACE_RECIPES_EVENT, newRecipes); context.emit(REPLACE_RECIPES_EVENT, newRecipes);
state.sortLoading = false; state.sortLoading = false;
@@ -427,6 +486,17 @@ export default defineNuxtComponent({
preferences.value.useMobileCards = !preferences.value.useMobileCards; preferences.value.useMobileCards = !preferences.value.useMobileCards;
} }
// Save scroll position when component is unmounted or when navigating away
onBeforeUnmount(() => {
recipeListState.saveScrollPosition();
window.removeEventListener("scroll", throttledScrollSave);
});
// Save scroll position when navigating to recipe pages
function handleRecipeNavigation() {
recipeListState.saveScrollPosition();
}
return { return {
...toRefs(state), ...toRefs(state),
displayTitleIcon, displayTitleIcon,
@@ -439,6 +509,7 @@ export default defineNuxtComponent({
sortRecipes, sortRecipes,
toggleMobileCards, toggleMobileCards,
useMobileCards, useMobileCards,
handleRecipeNavigation,
}; };
}, },
}); });

View File

@@ -0,0 +1,94 @@
import type { Recipe } from "~/lib/api/types/recipe";
import type { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
interface RecipeListState {
recipes: Recipe[];
page: number;
hasMore: boolean;
scrollPosition: number;
query: RecipeSearchQuery | null;
ready: boolean;
}
const recipeListStates = new Map<string, RecipeListState>();
function generateStateKey(query: RecipeSearchQuery | null): string {
if (!query) return "default";
const keyParts = [
query.search || "",
query.orderBy || "",
query.orderDirection || "",
query.queryFilter || "",
JSON.stringify(query.categories || []),
JSON.stringify(query.tags || []),
JSON.stringify(query.tools || []),
JSON.stringify(query.foods || []),
JSON.stringify(query.households || []),
];
return keyParts.join("|");
}
export function useRecipeListState(query: RecipeSearchQuery | null) {
const stateKey = generateStateKey(query);
// Initialize state if it doesn't exist
if (!recipeListStates.has(stateKey)) {
recipeListStates.set(stateKey, {
recipes: [],
page: 1,
hasMore: true,
scrollPosition: 0,
query,
ready: false,
});
}
const state = recipeListStates.get(stateKey)!;
function saveState(newState: Partial<RecipeListState>) {
Object.assign(state, newState);
}
function saveScrollPosition() {
state.scrollPosition = window.scrollY || document.documentElement.scrollTop || 0;
}
function restoreScrollPosition() {
if (state.scrollPosition > 0) {
// Use nextTick to ensure DOM is updated before scrolling
nextTick(() => {
window.scrollTo(0, state.scrollPosition);
});
}
}
function clearState() {
recipeListStates.delete(stateKey);
}
function hasValidState(): boolean {
return state.recipes.length > 0 && state.ready;
}
function isQueryMatch(newQuery: RecipeSearchQuery | null): boolean {
const newKey = generateStateKey(newQuery);
return newKey === stateKey;
}
return {
state: readonly(state),
saveState,
saveScrollPosition,
restoreScrollPosition,
clearState,
hasValidState,
isQueryMatch,
};
}
// Clean up old states when navigating away from recipe sections
export function cleanupRecipeListStates() {
recipeListStates.clear();
}

View File

@@ -288,7 +288,7 @@
"household-group": "Группа домохозяйства", "household-group": "Группа домохозяйства",
"household-management": "Управление домохозяйствами", "household-management": "Управление домохозяйствами",
"manage-households": "Управление Домами", "manage-households": "Управление Домами",
"admin-household-management": "Admin Household Management", "admin-household-management": "Управление Домохозяйством",
"admin-household-management-text": "Изменения в данном домохозяйстве будут отражены немедленно.", "admin-household-management-text": "Изменения в данном домохозяйстве будут отражены немедленно.",
"household-id-value": "Id домохозяйства: {0}", "household-id-value": "Id домохозяйства: {0}",
"private-household": "Частное домохозяйство", "private-household": "Частное домохозяйство",
@@ -298,7 +298,7 @@
"household-recipe-preferences": "Предпочтения для рецептов домашнего хозяйства", "household-recipe-preferences": "Предпочтения для рецептов домашнего хозяйства",
"default-recipe-preferences-description": "Это настройки по умолчанию, когда в вашем домашнем хозяйстве создается новый рецепт. Они могут быть изменены для отдельных рецептов в меню настроек рецепта.", "default-recipe-preferences-description": "Это настройки по умолчанию, когда в вашем домашнем хозяйстве создается новый рецепт. Они могут быть изменены для отдельных рецептов в меню настроек рецепта.",
"allow-users-outside-of-your-household-to-see-your-recipes": "Разрешить пользователям вне вашего домохозяйства видеть ваши рецепты", "allow-users-outside-of-your-household-to-see-your-recipes": "Разрешить пользователям вне вашего домохозяйства видеть ваши рецепты",
"allow-users-outside-of-your-household-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your household or with a pre-generated private link", "allow-users-outside-of-your-household-to-see-your-recipes-description": "При включении данной функции, вы можете делиться публичной ссылкой на данный рецепт, чтобы поделиться им с людьми без аккаунта. Если функция выключена — поделиться рецептом можно только с пользователями внутри вашего домохозяйства или по предварительно сгенерированной приватной ссылке",
"household-preferences": "Параметры домохозяйства" "household-preferences": "Параметры домохозяйства"
}, },
"meal-plan": { "meal-plan": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "mealie", "name": "mealie",
"version": "2.8.0", "version": "3.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "nuxt dev", "dev": "nuxt dev",

View File

@@ -9,5 +9,11 @@ import RecipeExplorerPage from "~/components/Domain/Recipe/RecipeExplorerPage.vu
export default defineNuxtComponent({ export default defineNuxtComponent({
components: { RecipeExplorerPage }, components: { RecipeExplorerPage },
setup() {
// Enable scroll restoration for this page to work with our state management
definePageMeta({
scrollToTop: false,
});
},
}); });
</script> </script>

View File

@@ -595,8 +595,8 @@
"broccoli slaw": { "broccoli slaw": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "broccoli slaw", "name": "Brokkolisalat",
"plural_name": "broccoli slaws" "plural_name": "Brokkolisalate"
}, },
"arbol chile pepper": { "arbol chile pepper": {
"aliases": [], "aliases": [],
@@ -1182,55 +1182,55 @@
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "Pluot", "name": "Pluot",
"plural_name": "pluots" "plural_name": "Pluots"
}, },
"soursop": { "soursop": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "soursop", "name": "Stachelannone",
"plural_name": "soursops" "plural_name": "Stachelannonen"
}, },
"hog plum": { "hog plum": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "hog plum", "name": "Mombinpflaumen",
"plural_name": "Mombinpflaume" "plural_name": "Mombinpflaume"
}, },
"bergamot orange": { "bergamot orange": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "bergamot orange", "name": "Bergamotte",
"plural_name": "bergamot oranges" "plural_name": "Bergamotten"
}, },
"luo han guo": { "luo han guo": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "luo han guo", "name": "Mönchsfrucht",
"plural_name": "luo han guos" "plural_name": "Mönchsfrüchte"
}, },
"mamey": { "mamey": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "mamey", "name": "Mammiapfel",
"plural_name": "mameys" "plural_name": "Mammiäpfel"
}, },
"sapote": { "sapote": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "sapote", "name": "Sapote",
"plural_name": "sapotes" "plural_name": "Sapote"
}, },
"green ume plum": { "green ume plum": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "green ume plum", "name": "Japanische Pflaume",
"plural_name": "green ume plums" "plural_name": "Japanische Pflaumen"
}, },
"kiwano": { "kiwano": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "kiwano", "name": "Horngurke",
"plural_name": "kiwanoes" "plural_name": "Horngurken"
} }
} }
}, },
@@ -1419,13 +1419,13 @@
"puffball": { "puffball": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "puffball", "name": "Riesenbovist",
"plural_name": "puffballs" "plural_name": "Riesenboviste"
}, },
"honey fungu": { "honey fungu": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "honey fungu", "name": "",
"plural_name": "honey fungus" "plural_name": "honey fungus"
}, },
"caesar's mushroom": { "caesar's mushroom": {
@@ -1471,8 +1471,8 @@
"cranberry": { "cranberry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "cranberry", "name": "Cranberry",
"plural_name": "cranberries" "plural_name": "Cranberries"
}, },
"cherry": { "cherry": {
"aliases": [], "aliases": [],
@@ -1483,7 +1483,7 @@
"blackberry": { "blackberry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "blackberry", "name": "Brombeere",
"plural_name": "Brombeeren" "plural_name": "Brombeeren"
}, },
"berry mix": { "berry mix": {
@@ -1495,8 +1495,8 @@
"maraschino cherry": { "maraschino cherry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "maraschino cherry", "name": "maraschino Kirsche",
"plural_name": "maraschino cherries" "plural_name": "maraschino Kirschen"
}, },
"dried cherry": { "dried cherry": {
"aliases": [], "aliases": [],
@@ -1507,8 +1507,8 @@
"juniper berry": { "juniper berry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "juniper berry", "name": "Wacholderbeere",
"plural_name": "juniper berries" "plural_name": "Wacholderbeeren"
}, },
"sour cherry": { "sour cherry": {
"aliases": [], "aliases": [],
@@ -1519,8 +1519,8 @@
"goji berry": { "goji berry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "goji berry", "name": "Goji-Beere",
"plural_name": "goji berries" "plural_name": "Goji-Beeren"
}, },
"dried blueberry": { "dried blueberry": {
"aliases": [], "aliases": [],
@@ -1561,8 +1561,8 @@
"mulberry": { "mulberry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "mulberry", "name": "Maulbeere",
"plural_name": "mulberries" "plural_name": "Maulbeeren"
}, },
"acai berry": { "acai berry": {
"aliases": [], "aliases": [],
@@ -1597,8 +1597,8 @@
"huckleberry": { "huckleberry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "huckleberry", "name": "Heidelbeere",
"plural_name": "huckleberries" "plural_name": "Heidelbeeren"
}, },
"dried elderberry": { "dried elderberry": {
"aliases": [], "aliases": [],
@@ -1615,8 +1615,8 @@
"dried berry": { "dried berry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "dried berry", "name": "getrocknete Beere",
"plural_name": "dried berries" "plural_name": "getrocknete Beeren"
}, },
"sea buckthorn": { "sea buckthorn": {
"aliases": [], "aliases": [],
@@ -1663,8 +1663,8 @@
"aronia berry": { "aronia berry": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "aronia berry", "name": "Apfelbeere",
"plural_name": "aronia berries" "plural_name": "Apfelbeeren"
}, },
"chokeberry": { "chokeberry": {
"aliases": [], "aliases": [],
@@ -1681,8 +1681,8 @@
"blackcurrant leaf": { "blackcurrant leaf": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "blackcurrant leaf", "name": "Schwarzes Johannisbeerblatt",
"plural_name": "blackcurrant leaves" "plural_name": "Schwarze Johannisbeerblätter"
}, },
"haskap berry": { "haskap berry": {
"aliases": [], "aliases": [],
@@ -1745,8 +1745,8 @@
"pine nut": { "pine nut": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "pine nut", "name": "Pinienkern",
"plural_name": "pine nuts" "plural_name": "Pinienkerne"
}, },
"pistachio": { "pistachio": {
"aliases": [], "aliases": [],
@@ -1776,7 +1776,7 @@
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "slivered almond", "name": "slivered almond",
"plural_name": "slivered almonds" "plural_name": "Mandelstifte"
}, },
"pumpkin seed": { "pumpkin seed": {
"aliases": [], "aliases": [],
@@ -1853,8 +1853,8 @@
"onion seed": { "onion seed": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "onion seed", "name": "Zwiebelsamen",
"plural_name": "onion seeds" "plural_name": "Zwiebelsamen"
}, },
"watermelon seed": { "watermelon seed": {
"aliases": [], "aliases": [],
@@ -1871,8 +1871,8 @@
"melon seed": { "melon seed": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "melon seed", "name": "Melonensamen",
"plural_name": "melon seeds" "plural_name": "Melonensamen"
}, },
"lotus seed": { "lotus seed": {
"aliases": [], "aliases": [],
@@ -1895,8 +1895,8 @@
"basil seed": { "basil seed": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "basil seed", "name": "Basilikumsamen",
"plural_name": "basil seeds" "plural_name": "Basilikumsamen"
}, },
"candlenut": { "candlenut": {
"aliases": [], "aliases": [],
@@ -1907,8 +1907,8 @@
"peanut brittle": { "peanut brittle": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "peanut brittle", "name": "Erdnusskrokant",
"plural_name": "peanut brittles" "plural_name": "Erdnusskrokant"
}, },
"jackfruit seed": { "jackfruit seed": {
"aliases": [], "aliases": [],

View File

@@ -241,106 +241,106 @@
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "artisjok", "name": "artisjok",
"plural_name": "artichokes" "plural_name": "artisjokken"
}, },
"new potato": { "new potato": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "new potato", "name": "krieltjes",
"plural_name": "new potatoes" "plural_name": "krieltjes"
}, },
"summer squash": { "summer squash": {
"aliases": [ "aliases": [
"courgette", "courgette",
"gem squash" "edelpompoen"
], ],
"description": "", "description": "",
"name": "summer squash", "name": "summer squash",
"plural_name": "summer squashes" "plural_name": "edelpompoenen"
}, },
"mixed green": { "mixed green": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "mixed green", "name": "gemengde groenten",
"plural_name": "mixed greens" "plural_name": "gemengde groenten"
}, },
"parsnip": { "parsnip": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "parsnip", "name": "pastinaak",
"plural_name": "parsnips" "plural_name": "pastinaken"
}, },
"baby carrot": { "baby carrot": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "baby carrot", "name": "baby wortel",
"plural_name": "baby carrots" "plural_name": "baby wortels"
}, },
"mixed vegetable": { "mixed vegetable": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "mixed vegetable", "name": "gemengde groente",
"plural_name": "mixed vegetables" "plural_name": "gemengde groenten"
}, },
"poblano pepper": { "poblano pepper": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "poblano pepper", "name": "poblano pepers",
"plural_name": "poblano peppers" "plural_name": "poblano pepers"
}, },
"sweet pepper": { "sweet pepper": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "sweet pepper", "name": "puntpaprika",
"plural_name": "sweet peppers" "plural_name": "puntpaprikas"
}, },
"serrano pepper": { "serrano pepper": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "serrano pepper", "name": "serranopeper",
"plural_name": "serrano peppers" "plural_name": "serranopepers"
}, },
"cayenne pepper": { "cayenne pepper": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "cayenne pepper", "name": "cayennepeper",
"plural_name": "cayenne peppers" "plural_name": "cayennepepers"
}, },
"green tomato": { "green tomato": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "green tomato", "name": "groene tomaat",
"plural_name": "green tomatoes" "plural_name": "groene tomaten"
}, },
"watercress": { "watercress": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "watercress", "name": "waterkers",
"plural_name": "watercress" "plural_name": "waterkersen"
}, },
"iceberg": { "iceberg": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "iceberg", "name": "ijsbergsla",
"plural_name": "icebergs" "plural_name": "ijsbergsla"
}, },
"mashed potato": { "mashed potato": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "mashed potato", "name": "aardappelpuree",
"plural_name": "mashed potatoes" "plural_name": "aardappelpurees"
}, },
"horseradish": { "horseradish": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "horseradish", "name": "mierikswortel",
"plural_name": "horseradishes" "plural_name": "mierikswortelen"
}, },
"chard": { "chard": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chard", "name": "snijbiet",
"plural_name": "chards" "plural_name": "snijbieten"
}, },
"pimiento": { "pimiento": {
"aliases": [], "aliases": [],
@@ -351,40 +351,40 @@
"spaghetti squash": { "spaghetti squash": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "spaghetti squash", "name": "spaghettipompoen",
"plural_name": "spaghetti squashes" "plural_name": "spaghettipompoenen"
}, },
"butter lettuce": { "butter lettuce": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "butter lettuce", "name": "kropsla",
"plural_name": "butter lettuces" "plural_name": "kropsla"
}, },
"hash brown": { "hash brown": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "hash brown", "name": "gehakte aardappel",
"plural_name": "hash browns" "plural_name": "gehakte aardappels"
}, },
"napa cabbage": { "napa cabbage": {
"aliases": [ "aliases": [
"chinese leaves" "chinese kool"
], ],
"description": "", "description": "",
"name": "napa cabbage", "name": "chinese kool",
"plural_name": "napa cabbages" "plural_name": "chinese kolen"
}, },
"celeriac": { "celeriac": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "celeriac", "name": "knolselderij",
"plural_name": "celeriacs" "plural_name": "knolselders"
}, },
"water chestnut": { "water chestnut": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "water chestnut", "name": "waterkastanje",
"plural_name": "water chestnuts" "plural_name": "waterkastanjes"
}, },
"turnip": { "turnip": {
"aliases": [], "aliases": [],
@@ -541,8 +541,8 @@
"ancho chile pepper": { "ancho chile pepper": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "ancho chile pepper", "name": "ancho peper",
"plural_name": "ancho chile peppers" "plural_name": "ancho pepers"
}, },
"microgreen": { "microgreen": {
"aliases": [], "aliases": [],
@@ -553,8 +553,8 @@
"boston lettuce": { "boston lettuce": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "boston lettuce", "name": "botersla",
"plural_name": "boston lettuces" "plural_name": "botersla"
}, },
"kohlrabi": { "kohlrabi": {
"aliases": [], "aliases": [],
@@ -608,19 +608,19 @@
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "golden beet", "name": "golden beet",
"plural_name": "golden beets" "plural_name": "gele biet"
}, },
"pea shoot": { "pea shoot": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "pea shoot", "name": "erwtenspruit",
"plural_name": "pea shoots" "plural_name": "erwtenspruiten"
}, },
"alfalfa": { "alfalfa": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "alfalfa", "name": "luzerne",
"plural_name": "alfalfas" "plural_name": "luzerne"
} }
} }
}, },
@@ -630,61 +630,61 @@
"aliases": [], "aliases": [],
"description": "Yes they are a fruit", "description": "Yes they are a fruit",
"name": "tomato", "name": "tomato",
"plural_name": "tomatoes" "plural_name": "tomaten"
}, },
"lemon": { "lemon": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "lemon", "name": "citroen",
"plural_name": "lemons" "plural_name": "citroenen"
}, },
"lime": { "lime": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "lime", "name": "limoen",
"plural_name": "limes" "plural_name": "limoenen"
}, },
"apple": { "apple": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "apple", "name": "appel",
"plural_name": "apples" "plural_name": "appels"
}, },
"banana": { "banana": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "banana", "name": "banaan",
"plural_name": "bananas" "plural_name": "bananen"
}, },
"orange": { "orange": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "orange", "name": "sinaasappel",
"plural_name": "oranges" "plural_name": "sinaasappels"
}, },
"raisin": { "raisin": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "raisin", "name": "rozijn",
"plural_name": "raisins" "plural_name": "rozijnen"
}, },
"pineapple": { "pineapple": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "pineapple", "name": "ananas",
"plural_name": "pineapples" "plural_name": "ananassen"
}, },
"mango": { "mango": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "mango", "name": "mango",
"plural_name": "mangoes" "plural_name": "mango's"
}, },
"peach": { "peach": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "peach", "name": "perzik",
"plural_name": "peaches" "plural_name": "perziken"
}, },
"date": { "date": {
"aliases": [], "aliases": [],
@@ -851,8 +851,8 @@
"dried fruit": { "dried fruit": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "dried fruit", "name": "gedroogd fruit",
"plural_name": "dried fruits" "plural_name": "gedroogd fruit"
}, },
"clementine": { "clementine": {
"aliases": [], "aliases": [],
@@ -4349,14 +4349,14 @@
"chicken breast": { "chicken breast": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chicken breast", "name": "kipfilet",
"plural_name": "chicken breasts" "plural_name": "kipfilet"
}, },
"chicken thigh": { "chicken thigh": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chicken thigh", "name": "kipdijfilet",
"plural_name": "chicken thighs" "plural_name": "kipdijfilet"
}, },
"cooked chicken": { "cooked chicken": {
"aliases": [], "aliases": [],
@@ -4385,14 +4385,14 @@
"chicken leg": { "chicken leg": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chicken leg", "name": "kippenpoot",
"plural_name": "chicken legs" "plural_name": "kippenpoten"
}, },
"chicken wing": { "chicken wing": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "chicken wing", "name": "kippenpoot",
"plural_name": "chicken wings" "plural_name": "kippenpoten"
}, },
"turkey breast": { "turkey breast": {
"aliases": [], "aliases": [],
@@ -8540,8 +8540,8 @@
"rice": { "rice": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "rice", "name": "rijst",
"plural_name": "rices" "plural_name": "rijst"
}, },
"Rice Krispie Cereal": { "Rice Krispie Cereal": {
"aliases": [ "aliases": [

View File

@@ -34,7 +34,7 @@
"zucchini": { "zucchini": {
"aliases": [], "aliases": [],
"description": "", "description": "",
"name": "zucchini", "name": "цукини",
"plural_name": "zucchinis" "plural_name": "zucchinis"
}, },
"potato": { "potato": {

6
poetry.lock generated
View File

@@ -1816,14 +1816,14 @@ signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
[[package]] [[package]]
name = "openai" name = "openai"
version = "1.94.0" version = "1.95.0"
description = "The official Python library for the openai API" description = "The official Python library for the openai API"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "openai-1.94.0-py3-none-any.whl", hash = "sha256:159c43b811669abe9bb4aafdc57a049966dfde2eac94b151aac3eb63bf9825b4"}, {file = "openai-1.95.0-py3-none-any.whl", hash = "sha256:a7afc9dca7e7d616371842af8ea6dbfbcb739a85d183f5f664ab1cc311b9ef18"},
{file = "openai-1.94.0.tar.gz", hash = "sha256:31c6c213cc80365d54632296c4aef7cda1800003ca5c784ac50a05d6bc05c197"}, {file = "openai-1.95.0.tar.gz", hash = "sha256:54bc42df9f7142312647dd485d34cca5df20af825fa64a30ca55164be2cf4cc9"},
] ]
[package.dependencies] [package.dependencies]

View File

@@ -3,7 +3,7 @@ authors = ["Hayden <hay-kot@pm.me>"]
description = "A Recipe Manager" description = "A Recipe Manager"
license = "AGPL" license = "AGPL"
name = "mealie" name = "mealie"
version = "2.8.0" version = "3.0.0"
include = [ include = [
# Explicit include to override .gitignore when packaging the frontend # Explicit include to override .gitignore when packaging the frontend
{ path = "mealie/frontend/**/*", format = ["sdist", "wheel"] } { path = "mealie/frontend/**/*", format = ["sdist", "wheel"] }