PageSourceSearch

https://crave-match.vercel.app/js/ai-mood.js?v=2.1

js crave-match.vercel.app collected 2026-10-03 06:34:34 UTC 2,947 bytes, 63 lines download raw bytes

1import { RECIPES } from './dataset.js';
2
3export function initAiMood() {
4  const inputEl = document.getElementById('ai-prompt-input');
5  const sendBtn = document.getElementById('ai-prompt-btn');
6  const chatContainer = document.getElementById('ai-chat-thread');
7
8  if (!inputEl || !sendBtn || !chatContainer) return;
9
10  sendBtn.addEventListener('click', handleUserPrompt);
11  inputEl.addEventListener('keypress', (e) => {
12    if (e.key === 'Enter') handleUserPrompt();
13  });
14
15  function handleUserPrompt() {
16    const text = inputEl.value.trim();
17    if (!text) return;
18
19    appendBubble(text, 'user');
20    inputEl.value = '';
21
22    setTimeout(() => {
23      processAiResponse(text);
24    }, 400);
25  }
26
27  function appendBubble(text, type) {
28    const bubble = document.createElement('div');
29    bubble.className = `ai-bubble ${type}`;
30    bubble.innerHTML = text;
31    chatContainer.appendChild(bubble);
32    chatContainer.scrollTop = chatContainer.scrollHeight;
33  }
34
35  function processAiResponse(prompt) {
36    const lower = prompt.toLowerCase();
37    let matches = [];
38
39    if (lower.includes('quick') || lower.includes('fast') || lower.includes('minute') || lower.includes('20')) {
40      matches = RECIPES.filter(r => (r.tags || r.recommendation_tags || []).includes('Quick & Easy') || (r.prepTimeMinutes && r.prepTimeMinutes <= 25));
41    } else if (lower.includes('protein') || lower.includes('gym') || lower.includes('muscle') || lower.includes('chicken')) {
42      matches = RECIPES.filter(r => (r.tags || r.recommendation_tags || []).includes('High-Protein') || (r.proteinG && r.proteinG >= 30));
43    } else if (lower.includes('comfort') || lower.includes('rainy') || lower.includes('cozy') || lower.includes('pasta')) {
44      matches = RECIPES.filter(r => (r.tags || r.recommendation_tags || []).includes('Comfort') || (r.tags || r.recommendation_tags || []).includes('Italian'));
45    } else if (lower.includes('spicy') || lower.includes('hot') || lower.includes('curry') || lower.includes('mexican')) {
46      matches = RECIPES.filter(r => (r.tags || r.recommendation_tags || []).includes('Spicy') || (r.tags || r.recommendation_tags || []).includes('Mexican'));
47    } else {
48      matches = RECIPES.slice(0, 3);
49    }
50
51    const topDish = matches[Math.floor(Math.random() * Math.min(matches.length, 5))] || RECIPES[0];
52    const topTag = (topDish.tags || topDish.recommendation_tags || ['Chef Pick'])[0];
53    const prep = topDish.prepTimeMinutes ? `${topDish.prepTimeMinutes} mins` : (topDish.prep_time || '25 mins');
54
55    const reply = `
56      ✨ Boosted <strong>${topTag}</strong> & dialed in your craving!<br/>
57      Tonight's top match: <strong style="color: var(--salmon);">${topDish.title}</strong> (${prep} • ${topDish.calories} kcal).<br/>
58      <span style="font-size: 0.8rem; color: var(--ink-secondary); font-family: var(--font-mono);">Calibrated with 98.4% taste alignment.</span>
59    `;
60
61    appendBubble(reply, 'assistant');
62  }
63}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.