This commit is contained in:
Ionel Andrei Cataon
2026-02-18 17:44:59 +02:00
parent 03281d71e4
commit 1607cb2ffc

View File

@@ -11,14 +11,11 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
:root { --primary: #3b82f6; --bg: #020617; --sidebar: #0f172a; --card: #1e293b; --accent: #60a5fa; --text: #ffffff; }
body.theme-dark { --bg: #020617; --sidebar: #0f172a; --primary: #3b82f6; }
body.theme-emerald { --bg: #064e3b; --sidebar: #065f46; --primary: #10b981; }
body.theme-midnight { --bg: #1e1b4b; --sidebar: #312e81; --primary: #818cf8; }
body.theme-titanium { --bg: #111827; --sidebar: #1f2937; --primary: #94a3b8; --card: #374151; --accent: #f8fafc; }
body { background-color: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif; height: 100dvh; overflow: hidden; transition: 0.3s; }
.main-wrapper { display: flex; height: 100dvh; flex-direction: row; }
@media (max-width: 991px) {
.main-wrapper { flex-direction: column; overflow-y: auto; height: auto; }
@@ -26,28 +23,21 @@
.chat-section { height: 600px !important; }
body { overflow-y: auto; }
}
.sidebar { width: 360px; background: var(--sidebar); border-right: 1px solid rgba(255,255,255,0.1); padding: 1.5rem; overflow-y: auto; flex-shrink: 0; }
.chat-section { flex-grow: 1; display: flex; flex-direction: column; background: rgba(0,0,0,0.2); position: relative; }
.label-pro { color: var(--accent); font-weight: 800; font-size: 0.75rem; text-transform: uppercase; margin-bottom: 5px; display: block; }
.glass-input { background: #fff !important; color: #000 !important; border: 3px solid var(--primary) !important; border-radius: 12px; font-weight: 800 !important; }
.stat-panel { background: linear-gradient(145deg, var(--primary), #1e3a8a); border-radius: 20px; padding: 1rem; margin: 10px 0; text-align: center; }
.water-hub { background: rgba(59, 130, 246, 0.1); border: 2px solid var(--primary); border-radius: 15px; padding: 12px; margin: 10px 0; text-align: center; }
.macro-hub { background: rgba(255,255,255,0.05); border-radius: 20px; padding: 15px; margin-top: 10px; }
.chat-container { flex-grow: 1; overflow-y: auto; padding: 1.5rem; display: flex; flex-direction: column; }
.msg { padding: 1.2rem; border-radius: 1.2rem; margin-bottom: 1rem; font-size: 1rem; max-width: 95%; color: #fff !important; word-wrap: break-word; }
.ai { background: var(--card); border-left: 6px solid var(--primary); align-self: flex-start; }
.user { background: var(--primary); align-self: flex-end; }
.top-ctrl { padding: 10px; display: flex; justify-content: flex-end; gap: 8px; background: rgba(0,0,0,0.3); }
.input-area { padding: 15px; background: var(--sidebar); border-top: 1px solid rgba(255,255,255,0.1); display: flex; gap: 10px; }
.btn-shop { background: #f59e0b; color: #000; font-weight: 800; border: none; border-radius: 12px; padding: 0 15px; white-space: nowrap; }
.btn-reset { background: rgba(239, 68, 68, 0.15); color: #fca5a5; border: 1px solid #ef4444; border-radius: 12px; padding: 8px; font-weight: 700; width: 100%; margin-top: 15px; }
table { width: 100%; display: block; overflow-x: auto; color: #fff; border-collapse: collapse; margin: 10px 0; background: rgba(0,0,0,0.2); border-radius: 8px; }
th, td { border: 1px solid rgba(255,255,255,0.1); padding: 8px; text-align: left; min-width: 80px; }
th { background: rgba(255,255,255,0.05); font-weight: 800; color: var(--accent); }
@@ -79,14 +69,11 @@
<input type="range" id="agg" class="form-range" min="-1000" max="1000" step="50" value="0" oninput="update()">
</div>
</div>
<div class="stat-panel"><h1 class="fw-bold mb-0" id="kcal">0</h1><p class="text-uppercase fw-bold m-0 opacity-75 small">Target Calories</p></div>
<div class="water-hub">
<h2 class="fw-bold text-info mb-0" id="h2o">0.0L</h2>
<span class="label-pro" id="txtH2O"><i class="fas fa-droplet me-1"></i> Daily Hydration</span>
</div>
<div class="macro-hub">
<canvas id="mChart" style="max-height: 120px;"></canvas>
<div class="row mt-2 text-center fw-bold small">
@@ -112,14 +99,12 @@
</ul>
</div>
</div>
<div id="chat" class="chat-container">
<div class="msg ai shadow">
<h5 class="fw-bold text-primary">System Online.</h5>
Meal plans are structured by day and meal, featuring raw ingredient macros and comprehensive totals.
Mathematical verification is now active. All meal plans are audited for calorie accuracy.
</div>
</div>
<div class="input-area">
<div class="input-group input-group-lg flex-grow-1">
<input type="text" id="uIn" class="form-control bg-dark text-white border-primary" placeholder="Type and press ENTER..." onkeydown="if(event.key === 'Enter') talk()">
@@ -146,7 +131,6 @@
if(w.value) w.value = isMetric ? (w.value/2.205).toFixed(0) : (w.value*2.205).toFixed(0);
if(t.value) t.value = isMetric ? (t.value/2.205).toFixed(0) : (t.value*2.205).toFixed(0);
if(h.value) h.value = isMetric ? (h.value*2.54).toFixed(0) : (h.value/2.54).toFixed(0);
document.getElementById('txtW').innerText = isMetric ? "Weight (kg)" : "Weight (lb)";
document.getElementById('txtT').innerText = isMetric ? "Target (kg)" : "Target (lb)";
document.getElementById('txtH').innerText = isMetric ? "Height (cm)" : "Height (in)";
@@ -161,11 +145,9 @@
const av = parseFloat(document.getElementById('age').value)||0, act = parseFloat(document.getElementById('act').value)||1;
const agg = parseInt(document.getElementById('agg').value);
if(!wv || !hv || !av) return;
const wc = isMetric ? wv : wv/2.205, hc = isMetric ? hv : hv*2.54;
const bmr = (10 * wc) + (6.25 * hc) - (5 * av) + 5;
const total = Math.round((bmr * act) + agg);
document.getElementById('kcal').innerText = total;
document.getElementById('h2o').innerText = isMetric ? (wc * 0.035).toFixed(1) + "L" : (wv * 0.6).toFixed(0) + "oz";
document.getElementById('aggDisp').innerText = (agg>0?'+':'')+agg+" kcal";
@@ -186,35 +168,31 @@
async function talk(custom) {
const box = document.getElementById('chat'), input = document.getElementById('uIn');
const isShopReq = custom === 'GENERATE_SHOPPING_LIST';
const text = isShopReq ? "GENERATE_SHOPPING_LIST: Provide a shopping list based on the active meal plan." : (custom || input.value);
const text = custom === 'GENERATE_SHOPPING_LIST' ? "GENERATE_SHOPPING_LIST: Provide a shopping list based on the active meal plan." : (custom || input.value);
if(!text) return;
if(!custom) { box.innerHTML += `<div class="msg user shadow-sm">${text}</div>`; input.value=""; }
const id = 'ai-'+Date.now(); box.innerHTML += `<div id="${id}" class="msg ai shadow">Analyzing nutrition data...</div>`;
const id = 'ai-'+Date.now(); box.innerHTML += `<div id="${id}" class="msg ai shadow">Auditing calories...</div>`;
box.scrollTop = box.scrollHeight;
const sysPrompt = {role:"system", content: `Expert Dietitian. TARGET: ${document.getElementById('kcal').innerText} kcal. SYSTEM: ${isMetric ? 'METRIC' : 'IMPERIAL'}.
CRITICAL MATHEMATICAL AUDIT:
1. You MUST ensure that (Meal 1 + Meal 2 + Meal 3 + Snacks) equals EXACTLY ${document.getElementById('kcal').innerText} kcal.
2. If the total is too low, you MUST increase the "Weight (RAW)" of primary ingredients (oats, rice, meat, etc.) until the sum is correct.
3. DO NOT fake the total. Every row in your table must mathematically add up to the 'Meal Total', and all 'Meal Totals' must add up to the 'Daily Total'.
MATH VERIFICATION RULE:
1. You are forbidden from hallucinating totals.
2. Before writing the 'Daily Total', you MUST manually sum the calories of every ingredient you listed.
3. If the sum is less than ${document.getElementById('kcal').innerText}, you MUST increase the Weight (RAW) of starch/protein sources (e.g., 100g -> 250g) until the math is perfect.
4. Meal Total MUST = Sum of ingredient calories.
5. Daily Total MUST = Sum of all Meal Totals.
STRUCTURE:
- Use ### [Day] and #### [Meal Name].
- Table columns: | Ingredient | Weight (RAW) | Calories | P(g) | C(g) | F(g) |.
- Provide: "Meal Total: [Kcal] | [P] | [C] | [F]" after every table.
- Final line per day: Bold **Daily Total: [Sum of all meals]**.
MANDATORY: All calculations must be based on RAW/DRY weights.`};
OUTPUT FORMAT:
- Table: | Ingredient | Weight (RAW) | Calories | P(g) | C(g) | F(g) |
- After each table: "Meal Total: [Kcal] | [P] | [C] | [F]"
- Final line: **Daily Total: [Sum] kcal**.`};
conversationHistory.push({role: "user", content: text});
try {
const res = await fetch("https://api.groq.com/openai/v1/chat/completions", {
method: "POST", headers: {"Content-Type":"application/json", "Authorization":`Bearer ${API}`},
body: JSON.stringify({ model:"llama-3.3-70b-versatile", messages:[sysPrompt, ...conversationHistory]})
body: JSON.stringify({ model:"llama-3.3-70b-versatile", messages:[sysPrompt, ...conversationHistory], temperature: 0.1})
});
const d = await res.json();
const aiMsg = d.choices[0].message.content;