threexlone/snap-sizzle
0
1document.addEventListener('DOMContentLoaded', () => {2 // DOM Elements3 const video = document.getElementById('video');4 const canvas = document.getElementById('canvas');5 const captureBtn = document.getElementById('capture-btn');6 const uploadBtn = document.getElementById('upload-btn');7 const fileInput = document.getElementById('file-input');8 const ingredientsList = document.getElementById('ingredients-list');9 const recipesSection = document.getElementById('recipes-section');10 const recipesContainer = document.getElementById('recipes-container');11 const cameraView = document.getElementById('camera-view');12 // Camera setup13 let stream = null;14 let isCameraOn = false;15 16 // Toggle camera on camera view click17 cameraView.addEventListener('click', async () => {18 if (isCameraOn) {19 stopCamera();20 } else {21 await startCamera();22 }23 });24 25 // Capture button click handler26 captureBtn.addEventListener('click', async () => {27 if (!isCameraOn) {28 try {29 await startCamera();30 return; // Let user try capturing again31 } catch (err) {32 console.error("Camera failed to start:", err);33 return;34 }35 }36 37 captureImage();38 });39 40 // Upload button click handler41 uploadBtn.addEventListener('click', () => {42 fileInput.click();43 });44 async function startCamera() {45 isCameraOn = true;46 try {47stream = await navigator.mediaDevices.getUserMedia({ video: true });48 video.srcObject = stream;49 video.classList.remove('hidden');50 cameraView.classList.add('hidden');51 captureBtn.disabled = false;52 } catch (err) {53 console.error("Error accessing camera:", err);54 alert("Could not access the camera. Please check permissions.");55 }56 }57 function stopCamera() {58 isCameraOn = false;59 if (stream) {60stream.getTracks().forEach(track => track.stop());61 stream = null;62 }63 video.classList.add('hidden');64 cameraView.classList.remove('hidden');65 captureBtn.disabled = true;66 }67 // Handle file input change68 fileInput.addEventListener('change', (e) => {69 if (e.target.files && e.target.files[0]) {70 const reader = new FileReader();71 reader.onload = async (event) => {72 // Stop camera if running73 if (isCameraOn) {74 stopCamera();75 }76 77 // Show uploaded image78 cameraView.innerHTML = `<img src="${event.target.result}" class="w-full h-full object-cover rounded">`;79 80 // Detect ingredients from image81 await detectIngredientsFromImage();82 };83 reader.readAsDataURL(e.target.files[0]);84 }85 });86 87 // Capture image function88 function captureImage() {89 if (!stream) return;90 91 canvas.width = video.videoWidth;92 canvas.height = video.videoHeight;93 const ctx = canvas.getContext('2d');94 ctx.drawImage(video, 0, 0, canvas.width, canvas.height);95 96 // Show captured image97 cameraView.innerHTML = '';98 cameraView.appendChild(canvas);99 video.classList.add('hidden');100 cameraView.classList.remove('hidden');101 102 // Detect ingredients103 simulateIngredientDetection();104 }105// Simulate ingredient detection (for demo purposes)106 function simulateIngredientDetection() {107 ingredientsList.innerHTML = '<div class="animate-pulse flex space-x-4"><div class="flex-1 space-y-4 py-1"><div class="h-4 bg-gray-200 rounded w-3/4"></div><div class="space-y-2"><div class="h-4 bg-gray-200 rounded"></div><div class="h-4 bg-gray-200 rounded w-5/6"></div></div></div></div>';108 109 // Simulate API delay110 setTimeout(() => {111 // Demo ingredients112 const demoIngredients = [113 'chicken breast',114 'bell pepper',115 'onion',116 'garlic',117 'olive oil',118 'salt',119 'black pepper'120 ];121 displayIngredients(demoIngredients);122 const recipes = await findRecipes(demoIngredients);123 displayRecipes(recipes);124}, 1500);125 }126 127 // Detect ingredients from image (simulated for demo)128 async function detectIngredientsFromImage() {129 try {130 simulateIngredientDetection();131 } catch (error) {132console.error('Error detecting ingredients:', error);133 ingredientsList.innerHTML = `134 <div class="text-center p-4">135 <i data-feather="alert-circle" class="w-12 h-12 text-red-500 mx-auto mb-2"></i>136 <h4 class="text-lg font-medium text-dark mb-1">Detection Failed</h4>137 <p class="text-gray-600 mb-4">We couldn't identify ingredients from your photo.</p>138 <div class="text-sm text-gray-500">139 <p class="mb-1">• Try taking a clearer photo with good lighting</p>140 <p class="mb-1">• Make sure ingredients are visible and not overlapping</p>141 <p>• Try cropping to focus on specific ingredients</p>142 </div>143 </div>144 `;145 feather.replace();146}147 }148 // Display detected ingredients149 function displayIngredients(ingredients) {150ingredientsList.innerHTML = '';151 ingredients.forEach(ingredient => {152 const tag = document.createElement('span');153 tag.className = 'ingredient-tag';154 tag.innerHTML = `<i data-feather="check-circle" class="w-3 h-3 mr-1"></i> ${ingredient}`;155 ingredientsList.appendChild(tag);156 });157 feather.replace();158 }159 // Find recipes based on ingredients (simulated for demo)160 // Find recipes based on ingredients (simulated for demo)161 async function findRecipes(ingredients) {162 // Show loading state163 recipesSection.classList.remove('hidden');164 recipesContainer.innerHTML = `165<div class="col-span-3 flex flex-col items-center justify-center py-12">166 <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary mb-4"></div>167 <p class="text-gray-600">Finding delicious recipes...</p>168 </div>169 `;170 return new Promise((resolve) => {171 setTimeout(() => {172 // Demo recipes data173 const demoRecipes = [174 {175 label: "Chicken Stir Fry",176 image: "http://static.photos/food/640x360/1",177 totalTime: 30,178 calories: 450,179 yield: 2,180 ingredientLines: [181 "2 chicken breasts",182 "1 bell pepper", 183 "1 onion",184 "2 cloves garlic",185 "2 tbsp olive oil"186 ],187 url: "#"188 },189 {190 label: "Grilled Chicken Salad",191 image: "http://static.photos/food/640x360/2",192 totalTime: 25,193 calories: 320,194 yield: 2,195 ingredientLines: [196 "2 chicken breasts",197 "mixed greens",198 "1 bell pepper",199 "1/2 onion",200 "olive oil dressing"201 ],202 url: "#"203 },204 {205 label: "Chicken Fajitas",206 image: "http://static.photos/food/640x360/3",207 totalTime: 40,208 calories: 520,209 yield: 4,210 ingredientLines: [211 "2 chicken breasts",212 "2 bell peppers",213 "1 onion",214 "fajita seasoning",215 "tortillas"216 ],217 url: "#"218 }219 ];220 resolve(demoRecipes);221 }, 1500);222 });223setTimeout(() => {224 // Demo recipes225 const demoRecipes = [226 {227 label: "Chicken Stir Fry",228 image: "http://static.photos/food/640x360/1",229 totalTime: 30,230 calories: 450,231 yield: 2,232 ingredientLines: [233 "2 chicken breasts",234 "1 bell pepper",235 "1 onion",236 "2 cloves garlic",237 "2 tbsp olive oil"238 ],239 url: "#"240 },241 {242 label: "Grilled Chicken Salad",243 image: "http://static.photos/food/640x360/2",244 totalTime: 25,245 calories: 320,246 yield: 2,247 ingredientLines: [248 "2 chicken breasts",249 "mixed greens",250 "1 bell pepper",251 "1/2 onion",252 "olive oil dressing"253 ],254 url: "#"255 },256 {257 label: "Chicken Fajitas",258 image: "http://static.photos/food/640x360/3",259 totalTime: 40,260 calories: 520,261 yield: 4,262 ingredientLines: [263 "2 chicken breasts",264 "2 bell peppers",265 "1 onion",266 "fajita seasoning",267 "tortillas"268 ],269 url: "#"270 }271 ];272 try {273 const recipes = await findRecipes(ingredients);274 275 // Clear previous results276 recipesContainer.innerHTML = '';277 278 // Display recipes279 recipes.forEach(recipe => {280 const recipeCard = document.createElement('div');281 recipeCard.className = 'recipe-card bg-white rounded-xl shadow-md overflow-hidden';282 recipeCard.innerHTML = `283<img src="${recipe.image}" alt="${recipe.label}" class="w-full h-48 object-cover">284 <div class="p-6">285 <h3 class="text-xl font-semibold text-dark mb-2">${recipe.label}</h3>286 <div class="flex items-center text-sm text-gray-500 mb-3">287 <span class="flex items-center mr-4"><i data-feather="clock" class="w-4 h-4 mr-1"></i> ${Math.round(recipe.totalTime)} mins</span>288 <span class="flex items-center"><i data-feather="activity" class="w-4 h-4 mr-1"></i> ${recipe.calories ? Math.round(recipe.calories/recipe.yield) : 'N/A'} cal/serving</span>289 </div>290 <div class="mb-4">291 ${recipe.ingredientLines.slice(0, 3).map(ing => `<span class="ingredient-tag">${ing}</span>`).join('')}292 </div>293 <a href="${recipe.url}" class="block w-full bg-accent hover:bg-yellow-500 text-dark font-medium py-2 px-4 rounded-lg transition text-center">294 View Recipe295 </a>296</div>297 `;298 recipesContainer.appendChild(recipeCard);299 });300});301 recipesSection.classList.remove('hidden');302 feather.replace();303 } catch (error) {304 console.error('Error finding recipes:', error);305 recipesContainer.innerHTML = `306 <div class="text-center col-span-3 py-8">307 <i data-feather="frown" class="w-12 h-12 text-red-500 mx-auto mb-4"></i>308 <h3 class="text-xl font-medium text-dark mb-2">Recipe Search Failed</h3>309 <p class="text-gray-600 mb-4">We couldn't find recipes matching your ingredients.</p>310 <button onclick="window.location.reload()" class="bg-primary hover:bg-red-600 text-white px-6 py-2 rounded-lg flex items-center gap-2 transition mx-auto">311 <i data-feather="refresh-cw"></i> Try Again312 </button>313 </div>314 `;315 feather.replace();316}317 }318// Clean up camera on page exit319 window.addEventListener('beforeunload', () => {320 stopCamera();321 });322});