s
This commit is contained in:
@@ -11,14 +11,11 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
<style>
|
<style>
|
||||||
:root { --primary: #3b82f6; --bg: #020617; --sidebar: #0f172a; --card: #1e293b; --accent: #60a5fa; --text: #ffffff; }
|
:root { --primary: #3b82f6; --bg: #020617; --sidebar: #0f172a; --card: #1e293b; --accent: #60a5fa; --text: #ffffff; }
|
||||||
|
|
||||||
body.theme-dark { --bg: #020617; --sidebar: #0f172a; --primary: #3b82f6; }
|
body.theme-dark { --bg: #020617; --sidebar: #0f172a; --primary: #3b82f6; }
|
||||||
body.theme-emerald { --bg: #064e3b; --sidebar: #065f46; --primary: #10b981; }
|
body.theme-emerald { --bg: #064e3b; --sidebar: #065f46; --primary: #10b981; }
|
||||||
body.theme-midnight { --bg: #1e1b4b; --sidebar: #312e81; --primary: #818cf8; }
|
body.theme-midnight { --bg: #1e1b4b; --sidebar: #312e81; --primary: #818cf8; }
|
||||||
body.theme-titanium { --bg: #111827; --sidebar: #1f2937; --primary: #94a3b8; --card: #374151; --accent: #f8fafc; }
|
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; }
|
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; }
|
.main-wrapper { display: flex; height: 100dvh; flex-direction: row; }
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 991px) {
|
||||||
.main-wrapper { flex-direction: column; overflow-y: auto; height: auto; }
|
.main-wrapper { flex-direction: column; overflow-y: auto; height: auto; }
|
||||||
@@ -26,28 +23,21 @@
|
|||||||
.chat-section { height: 600px !important; }
|
.chat-section { height: 600px !important; }
|
||||||
body { overflow-y: auto; }
|
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; }
|
.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; }
|
.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; }
|
.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; }
|
.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; }
|
.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; }
|
.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; }
|
.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; }
|
.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; }
|
.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; }
|
.ai { background: var(--card); border-left: 6px solid var(--primary); align-self: flex-start; }
|
||||||
.user { background: var(--primary); align-self: flex-end; }
|
.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); }
|
.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; }
|
.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-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; }
|
.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; }
|
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, 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); }
|
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()">
|
<input type="range" id="agg" class="form-range" min="-1000" max="1000" step="50" value="0" oninput="update()">
|
||||||
</div>
|
</div>
|
||||||
</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="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">
|
<div class="water-hub">
|
||||||
<h2 class="fw-bold text-info mb-0" id="h2o">0.0L</h2>
|
<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>
|
<span class="label-pro" id="txtH2O"><i class="fas fa-droplet me-1"></i> Daily Hydration</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="macro-hub">
|
<div class="macro-hub">
|
||||||
<canvas id="mChart" style="max-height: 120px;"></canvas>
|
<canvas id="mChart" style="max-height: 120px;"></canvas>
|
||||||
<div class="row mt-2 text-center fw-bold small">
|
<div class="row mt-2 text-center fw-bold small">
|
||||||
@@ -112,14 +99,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="chat" class="chat-container">
|
<div id="chat" class="chat-container">
|
||||||
<div class="msg ai shadow">
|
<div class="msg ai shadow">
|
||||||
<h5 class="fw-bold text-primary">System Online.</h5>
|
<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>
|
</div>
|
||||||
|
|
||||||
<div class="input-area">
|
<div class="input-area">
|
||||||
<div class="input-group input-group-lg flex-grow-1">
|
<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()">
|
<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(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(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);
|
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('txtW').innerText = isMetric ? "Weight (kg)" : "Weight (lb)";
|
||||||
document.getElementById('txtT').innerText = isMetric ? "Target (kg)" : "Target (lb)";
|
document.getElementById('txtT').innerText = isMetric ? "Target (kg)" : "Target (lb)";
|
||||||
document.getElementById('txtH').innerText = isMetric ? "Height (cm)" : "Height (in)";
|
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 av = parseFloat(document.getElementById('age').value)||0, act = parseFloat(document.getElementById('act').value)||1;
|
||||||
const agg = parseInt(document.getElementById('agg').value);
|
const agg = parseInt(document.getElementById('agg').value);
|
||||||
if(!wv || !hv || !av) return;
|
if(!wv || !hv || !av) return;
|
||||||
|
|
||||||
const wc = isMetric ? wv : wv/2.205, hc = isMetric ? hv : hv*2.54;
|
const wc = isMetric ? wv : wv/2.205, hc = isMetric ? hv : hv*2.54;
|
||||||
const bmr = (10 * wc) + (6.25 * hc) - (5 * av) + 5;
|
const bmr = (10 * wc) + (6.25 * hc) - (5 * av) + 5;
|
||||||
const total = Math.round((bmr * act) + agg);
|
const total = Math.round((bmr * act) + agg);
|
||||||
|
|
||||||
document.getElementById('kcal').innerText = total;
|
document.getElementById('kcal').innerText = total;
|
||||||
document.getElementById('h2o').innerText = isMetric ? (wc * 0.035).toFixed(1) + "L" : (wv * 0.6).toFixed(0) + "oz";
|
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";
|
document.getElementById('aggDisp').innerText = (agg>0?'+':'')+agg+" kcal";
|
||||||
@@ -186,35 +168,31 @@
|
|||||||
|
|
||||||
async function talk(custom) {
|
async function talk(custom) {
|
||||||
const box = document.getElementById('chat'), input = document.getElementById('uIn');
|
const box = document.getElementById('chat'), input = document.getElementById('uIn');
|
||||||
const isShopReq = custom === 'GENERATE_SHOPPING_LIST';
|
const text = custom === 'GENERATE_SHOPPING_LIST' ? "GENERATE_SHOPPING_LIST: Provide a shopping list based on the active meal plan." : (custom || input.value);
|
||||||
const text = isShopReq ? "GENERATE_SHOPPING_LIST: Provide a shopping list based on the active meal plan." : (custom || input.value);
|
|
||||||
if(!text) return;
|
if(!text) return;
|
||||||
|
|
||||||
if(!custom) { box.innerHTML += `<div class="msg user shadow-sm">${text}</div>`; input.value=""; }
|
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;
|
box.scrollTop = box.scrollHeight;
|
||||||
|
|
||||||
const sysPrompt = {role:"system", content: `Expert Dietitian. TARGET: ${document.getElementById('kcal').innerText} kcal. SYSTEM: ${isMetric ? 'METRIC' : 'IMPERIAL'}.
|
const sysPrompt = {role:"system", content: `Expert Dietitian. TARGET: ${document.getElementById('kcal').innerText} kcal. SYSTEM: ${isMetric ? 'METRIC' : 'IMPERIAL'}.
|
||||||
|
|
||||||
CRITICAL MATHEMATICAL AUDIT:
|
MATH VERIFICATION RULE:
|
||||||
1. You MUST ensure that (Meal 1 + Meal 2 + Meal 3 + Snacks) equals EXACTLY ${document.getElementById('kcal').innerText} kcal.
|
1. You are forbidden from hallucinating totals.
|
||||||
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.
|
2. Before writing the 'Daily Total', you MUST manually sum the calories of every ingredient you listed.
|
||||||
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'.
|
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:
|
OUTPUT FORMAT:
|
||||||
- Use ### [Day] and #### [Meal Name].
|
- Table: | Ingredient | Weight (RAW) | Calories | P(g) | C(g) | F(g) |
|
||||||
- Table columns: | Ingredient | Weight (RAW) | Calories | P(g) | C(g) | F(g) |.
|
- After each table: "Meal Total: [Kcal] | [P] | [C] | [F]"
|
||||||
- Provide: "Meal Total: [Kcal] | [P] | [C] | [F]" after every table.
|
- Final line: **Daily Total: [Sum] kcal**.`};
|
||||||
- Final line per day: Bold **Daily Total: [Sum of all meals]**.
|
|
||||||
|
|
||||||
MANDATORY: All calculations must be based on RAW/DRY weights.`};
|
|
||||||
|
|
||||||
conversationHistory.push({role: "user", content: text});
|
conversationHistory.push({role: "user", content: text});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch("https://api.groq.com/openai/v1/chat/completions", {
|
const res = await fetch("https://api.groq.com/openai/v1/chat/completions", {
|
||||||
method: "POST", headers: {"Content-Type":"application/json", "Authorization":`Bearer ${API}`},
|
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 d = await res.json();
|
||||||
const aiMsg = d.choices[0].message.content;
|
const aiMsg = d.choices[0].message.content;
|
||||||
|
|||||||
Reference in New Issue
Block a user