@@ -3,6 +3,8 @@ const status = document.querySelector("#status");
|
||||
const list = document.querySelector("#recipes");
|
||||
const search = document.querySelector("#search");
|
||||
const exit = document.querySelector("#exit");
|
||||
const resume = document.querySelector("#resume");
|
||||
const themeToggle = document.querySelector("#theme-toggle");
|
||||
const portionControls = document.querySelector("#portions");
|
||||
const portionCount = document.querySelector("#portion-count");
|
||||
const fewerPortions = document.querySelector("#fewer-portions");
|
||||
@@ -19,6 +21,9 @@ let displayedRecipe = null;
|
||||
let portions = null;
|
||||
let progress = { completedIngredients: [], completedSteps: [] };
|
||||
let recipeModeActive = false;
|
||||
let canResume = false;
|
||||
let lastRecipeName = null;
|
||||
let theme = "dark";
|
||||
|
||||
async function request(path, options) {
|
||||
const response = await fetch(`${apiBase}${path}`, {
|
||||
@@ -56,6 +61,9 @@ function render() {
|
||||
portionControls.hidden = !recipeModeActive;
|
||||
portionCount.textContent = portions ?? "";
|
||||
fewerPortions.disabled = portions <= 1;
|
||||
resume.hidden = !canResume;
|
||||
resume.textContent = lastRecipeName ? `Resume ${lastRecipeName}` : "Resume last recipe";
|
||||
themeToggle.textContent = theme === "light" ? "Use dark mode" : "Use light mode";
|
||||
renderActiveRecipe();
|
||||
}
|
||||
|
||||
@@ -112,6 +120,9 @@ function applyState(state) {
|
||||
displayedRecipe = recipeModeActive ? state.recipe : null;
|
||||
portions = state.portions;
|
||||
progress = state.progress || { completedIngredients: [], completedSteps: [] };
|
||||
canResume = Boolean(state.canResume);
|
||||
lastRecipeName = state.lastRecipeName || null;
|
||||
theme = state.theme === "light" ? "light" : "dark";
|
||||
}
|
||||
|
||||
async function activate(recipe) {
|
||||
@@ -161,6 +172,27 @@ async function updateProgress(kind, index, completed) {
|
||||
fewerPortions.addEventListener("click", () => adjustPortions(-1));
|
||||
morePortions.addEventListener("click", () => adjustPortions(1));
|
||||
|
||||
resume.addEventListener("click", async () => {
|
||||
try {
|
||||
applyState(await request("/resume", { method: "POST", body: "{}" }));
|
||||
status.textContent = `${activeRecipeLabel} is back on the mirror.`;
|
||||
render();
|
||||
} catch (error) {
|
||||
status.textContent = error.message;
|
||||
}
|
||||
});
|
||||
|
||||
themeToggle.addEventListener("click", async () => {
|
||||
try {
|
||||
applyState(await request("/theme", {
|
||||
method: "POST", body: JSON.stringify({ theme: theme === "light" ? "dark" : "light" })
|
||||
}));
|
||||
render();
|
||||
} catch (error) {
|
||||
status.textContent = error.message;
|
||||
}
|
||||
});
|
||||
|
||||
exit.addEventListener("click", async () => {
|
||||
status.textContent = "Leaving recipe mode…";
|
||||
try {
|
||||
|
||||
+5
-1
@@ -13,7 +13,11 @@
|
||||
<p class="eyebrow">Kitchen Mirror</p>
|
||||
<h1>Recipes</h1>
|
||||
</div>
|
||||
<button id="exit" class="secondary" type="button">Exit recipe mode</button>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="secondary" type="button"></button>
|
||||
<button id="resume" class="secondary" type="button" hidden>Resume last recipe</button>
|
||||
<button id="exit" class="secondary" type="button">Exit recipe mode</button>
|
||||
</div>
|
||||
</header>
|
||||
<p id="status" role="status">Loading recipes…</p>
|
||||
<div id="portions" class="portions" hidden>
|
||||
|
||||
@@ -23,6 +23,8 @@ header {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
|
||||
|
||||
h1 { margin: 0; font-size: 2.3rem; }
|
||||
|
||||
.eyebrow {
|
||||
|
||||
Reference in New Issue
Block a user