This commit is contained in:
Ionel Andrei Cataon
2026-02-18 16:42:50 +02:00
parent 9965f28ce1
commit 9c4a8cfa62

View File

@@ -3,198 +3,202 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NutriAI Elite v8.0 - Professional Nutritionist</title> <title>NutriAI Elite v11.0 - Shopping & Macros</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
<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 { :root {
--primary: #3b82f6; --primary: #3b82f6; --bg: #0f172a; --sidebar-bg: #1e293b;
--bg: #0f172a; --input-bg: #ffffff; --input-text: #000000; --card-ai: #334155;
--sidebar-bg: #1e293b;
--input-bg: #ffffff; /* Stark white for input contrast */
--input-text: #000000; /* Deep black for input readability */
} }
body { background-color: var(--bg); color: #ffffff; font-family: 'Outfit', sans-serif; transition: 0.4s; } body.theme-dark { --bg: #020617; --sidebar-bg: #0f172a; --card-ai: #1e293b; }
body.theme-emerald { --bg: #064e3b; --sidebar-bg: #065f46; --primary: #10b981; --card-ai: #065f46; }
body.theme-midnight { --bg: #1e1b4b; --sidebar-bg: #312e81; --primary: #818cf8; --card-ai: #312e81; }
body.theme-carbon { --bg: #171717; --sidebar-bg: #262626; --primary: #f5f5f5; --card-ai: #262626; }
/* Sidebar Styling */ body { background-color: var(--bg); color: #ffffff; font-family: 'Outfit', sans-serif; transition: 0.5s; overflow-x: hidden; }
.sidebar { background: var(--sidebar-bg); border-right: 1px solid rgba(255,255,255,0.1); min-height: 100vh; padding: 2rem; } .sidebar { background: var(--sidebar-bg); border-right: 1px solid rgba(255,255,255,0.1); min-height: 100vh; padding: 1.5rem; box-shadow: 10px 0 30px rgba(0,0,0,0.2); }
/* High Contrast Labels and Inputs */ .label-pro { color: var(--primary); font-weight: 800; font-size: 0.75rem; text-transform: uppercase; margin-bottom: 5px; display: block; }
.label-pro { color: #60a5fa; font-weight: 800; font-size: 0.75rem; text-transform: uppercase; margin-bottom: 5px; display: block; } .glass-input { background: var(--input-bg) !important; color: var(--input-text) !important; border: 2px solid var(--primary) !important; border-radius: 8px; font-weight: 700 !important; }
.glass-input {
background: var(--input-bg) !important;
color: var(--input-text) !important;
border: 2px solid var(--primary) !important;
border-radius: 8px; font-weight: 600 !important;
}
/* Calorie Hub */ .kcal-hub { background: linear-gradient(135deg, var(--primary) 0%, #1e40af 100%); border-radius: 15px; padding: 1.2rem; text-align: center; margin-top: 15px; }
.kcal-hub {
background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);
border-radius: 15px; padding: 1.5rem; text-align: center; margin-top: 20px;
box-shadow: 0 10px 25px rgba(37, 99, 235, 0.3);
}
/* Chat Area & Markdown Tables */ .macro-card { background: rgba(0,0,0,0.2); border-radius: 15px; padding: 15px; margin-top: 15px; border: 1px solid rgba(255,255,255,0.05); }
.chat-area { height: 60vh; overflow-y: auto; padding: 25px; border-radius: 20px; background: rgba(0,0,0,0.2); }
.msg { padding: 1.5rem; border-radius: 1.2rem; margin-bottom: 1.5rem; max-width: 90%; line-height: 1.6; }
.ai { background: #334155; border-left: 5px solid #3b82f6; color: #ffffff; }
.user { background: #10b981; margin-left: auto; color: white; font-weight: 600; }
/* Groq Professional Table Styling */ .chat-area { height: 52vh; overflow-y: auto; padding: 25px; border-radius: 20px; background: rgba(0,0,0,0.2); }
table { width: 100%; margin: 15px 0; border-collapse: collapse; background: #0f172a; color: white; border-radius: 8px; overflow: hidden; } .msg { padding: 1.5rem; border-radius: 1.2rem; margin-bottom: 1.5rem; max-width: 95%; line-height: 1.6; }
th, td { padding: 12px; border: 1px solid #334155; text-align: left; } .ai { background: var(--card-ai); border-left: 6px solid var(--primary); }
th { background: #3b82f6; } .user { background: var(--primary); margin-left: auto; font-weight: 600; }
.input-bar { background: #1e293b; border-radius: 50px; padding: 10px 25px; border: 2px solid var(--primary); } table { width: 100%; margin: 15px 0; border-collapse: collapse; background: rgba(0,0,0,0.3); border-radius: 10px; overflow: hidden; }
#userInput { color: white !important; font-size: 1.1rem; } th, td { padding: 12px; border: 1px solid rgba(255,255,255,0.1); }
th { background: var(--primary); color: #000; }
.input-bar { background: var(--sidebar-bg); border-radius: 50px; padding: 10px 25px; border: 2px solid var(--primary); }
.top-nav { position: fixed; top: 15px; right: 20px; z-index: 1000; display: flex; gap: 10px; }
.btn-shop { background: #f59e0b; color: #000; font-weight: 800; border: none; border-radius: 12px; padding: 10px 20px; transition: 0.3s; }
.btn-shop:hover { background: #fbbf24; transform: scale(1.05); }
</style> </style>
</head> </head>
<body> <body class="theme-dark">
<div class="top-nav">
<div class="btn-group shadow-lg">
<input type="radio" class="btn-check" name="units" id="metric" checked onchange="toggleUnits()">
<label class="btn btn-primary px-3" for="metric">Metric</label>
<input type="radio" class="btn-check" name="units" id="imperial" onchange="toggleUnits()">
<label class="btn btn-primary px-3" for="imperial">Imperial</label>
</div>
<div class="dropdown">
<button class="btn btn-light rounded-circle shadow" data-bs-toggle="dropdown" style="width: 45px; height: 45px;"><i class="fas fa-palette"></i></button>
<ul class="dropdown-menu dropdown-menu-end shadow-lg">
<li><a class="dropdown-item" href="#" onclick="setTheme('dark')">Onyx Dark</a></li>
<li><a class="dropdown-item" href="#" onclick="setTheme('emerald')">Emerald Forest</a></li>
<li><a class="dropdown-item" href="#" onclick="setTheme('midnight')">Midnight Indigo</a></li>
<li><a class="dropdown-item" href="#" onclick="setTheme('carbon')">Carbon White</a></li>
</ul>
</div>
</div>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-lg-3 sidebar"> <div class="col-lg-3 sidebar">
<h3 class="fw-bold mb-4 text-primary"><i class="fas fa-dna me-2"></i>NutriAI <span class="text-white">Elite</span></h3> <h4 class="fw-bold mb-3"><i class="fas fa-microchip text-primary me-2"></i>NutriAI Elite</h4>
<div class="row g-3 mb-4"> <div class="row g-2 mb-3">
<div class="col-6"> <div class="col-6"><label class="label-pro" id="lblCurW">Weight (kg)</label><input type="number" id="weight" class="form-control glass-input" value="85" oninput="updateProfile()"></div>
<label class="label-pro">Current (kg)</label> <div class="col-6"><label class="label-pro" id="lblTarW">Target (kg)</label><input type="number" id="targetW" class="form-control glass-input" value="75" oninput="updateProfile()"></div>
<input type="number" id="weight" class="form-control glass-input" value="85" oninput="updateProfile()"> <div class="col-6"><label class="label-pro" id="lblH">Height (cm)</label><input type="number" id="height" class="form-control glass-input" value="180" oninput="updateProfile()"></div>
</div> <div class="col-6"><label class="label-pro">Age</label><input type="number" id="age" class="form-control glass-input" value="30" oninput="updateProfile()"></div>
<div class="col-6"> <div class="col-12"><label class="label-pro">Activity</label>
<label class="label-pro">Target (kg)</label>
<input type="number" id="targetW" class="form-control glass-input" value="75" oninput="updateProfile()">
</div>
<div class="col-6">
<label class="label-pro">Height (cm)</label>
<input type="number" id="height" class="form-control glass-input" value="180" oninput="updateProfile()">
</div>
<div class="col-6">
<label class="label-pro">Age</label>
<input type="number" id="age" class="form-control glass-input" value="30" oninput="updateProfile()">
</div>
<div class="col-12">
<label class="label-pro">Biological Sex</label>
<select id="sex" class="form-select glass-input" onchange="updateProfile()">
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
<div class="col-12">
<label class="label-pro">Activity Level</label>
<select id="activity" class="form-select glass-input" onchange="updateProfile()"> <select id="activity" class="form-select glass-input" onchange="updateProfile()">
<option value="1.2">Sedentary (Office/No Exercise)</option> <option value="1.2">Sedentary</option><option value="1.375">Light</option><option value="1.55" selected>Moderate</option><option value="1.725">Active</option>
<option value="1.375">Light (1-2 days sport)</option>
<option value="1.55">Moderate (3-5 days sport)</option>
<option value="1.725">Very Active (Daily Heavy)</option>
</select> </select>
</div> </div>
<div class="col-12"> <div class="col-12">
<label class="label-pro">Daily Deficit/Surplus</label> <label class="label-pro d-flex justify-content-between">Aggressiveness <span><span id="aggValue">-500</span> kcal</span></label>
<select id="deficit" class="form-select glass-input" onchange="updateProfile()"> <input type="range" id="aggSlider" class="form-range" min="-1000" max="1000" step="50" value="-500" oninput="updateProfile()">
<option value="-1000">Extreme Loss (-1000 kcal)</option>
<option value="-500" selected>Steady Loss (-500 kcal)</option>
<option value="0">Maintenance</option>
<option value="500">Mass Gain (+500 kcal)</option>
</select>
</div> </div>
</div> </div>
<div class="kcal-hub shadow-lg"> <div class="kcal-hub shadow">
<small class="text-uppercase fw-bold opacity-75">Your Calculated Goal</small> <small class="text-uppercase fw-bold opacity-75">Daily Target</small>
<h1 class="fw-bold display-5 mb-0" id="totalKcal">2500</h1> <h2 class="fw-bold mb-0" id="totalKcal">2500</h2>
<span class="small fw-bold">KCAL / DAY</span> <span class="small">KCAL / DAY</span>
</div>
<div class="macro-card text-center">
<canvas id="macroChart" style="max-height: 120px;"></canvas>
<div class="d-flex justify-content-around mt-2 small fw-bold">
<span class="text-info">P: <span id="pGram">0</span>g</span>
<span class="text-danger">C: <span id="cGram">0</span>g</span>
<span class="text-warning">F: <span id="fGram">0</span>g</span>
</div>
</div> </div>
<p class="mt-3 small text-center opacity-50">Maintenance Level: <span id="maintVal">0</span> kcal</p>
</div> </div>
<div class="col-lg-9 p-5"> <div class="col-lg-9 p-5">
<h2 class="fw-bold mb-4">Elite AI Coaching Strategy</h2>
<div id="chatBox" class="chat-area shadow-inner"> <div id="chatBox" class="chat-area shadow-inner">
<div class="msg ai shadow-sm"> <div class="msg ai">
<h5 class="fw-bold">Elite Protocol Ready.</h5> <h5 class="fw-bold text-primary">System Online.</h5>
I have synced your current data (<span class="text-info" id="curTag">85</span>kg) and target (<span class="text-info" id="tarTag">75</span>kg). <br> All biometrics synced. You are currently in <strong><span id="aggStatus"></span></strong> mode.
<strong>How would you like to structure your diet today?</strong> Ask for a table-based meal plan. Ask for a meal plan, and then I can generate your shopping list!
</div> </div>
</div> </div>
<div class="input-bar d-flex align-items-center mt-4"> <div class="mt-4 d-flex gap-2">
<input type="text" id="userInput" class="form-control bg-transparent border-0 text-white" <div class="input-bar d-flex align-items-center flex-grow-1 shadow">
placeholder="Message your coach (Press ENTER)..."> <input type="text" id="userInput" class="form-control bg-transparent border-0 text-white" placeholder="Message your coach...">
<button onclick="sendToGroq()" class="btn btn-primary rounded-circle p-3 ms-2" style="width: 55px; height: 55px;"> <button onclick="sendToGroq()" class="btn btn-primary rounded-circle p-2 ms-2" style="width: 45px; height: 45px;"><i class="fas fa-paper-plane"></i></button>
<i class="fas fa-paper-plane"></i> </div>
<button onclick="generateShoppingList()" class="btn-shop shadow">
<i class="fas fa-shopping-cart me-2"></i> SHOPPING LIST
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
const GROQ_API_KEY = "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"; const GROQ_API_KEY = "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";
let isMetric = true;
let macroChart;
function setTheme(t) { document.body.className = 'theme-' + t; }
document.getElementById('userInput').addEventListener('keypress', (e) => { if(e.key === 'Enter') sendToGroq(); }); document.getElementById('userInput').addEventListener('keypress', (e) => { if(e.key === 'Enter') sendToGroq(); });
function updateProfile() { function initChart() {
const w = parseFloat(document.getElementById('weight').value) || 0; const ctx = document.getElementById('macroChart').getContext('2d');
const tw = parseFloat(document.getElementById('targetW').value) || 0; macroChart = new Chart(ctx, {
const h = parseFloat(document.getElementById('height').value) || 0; type: 'doughnut',
const a = parseFloat(document.getElementById('age').value) || 0; data: { labels: ['Protein', 'Carbs', 'Fats'], datasets: [{ data: [30, 40, 30], backgroundColor: ['#0dcaf0', '#dc3545', '#ffc107'], borderWeight: 0 }] },
const s = document.getElementById('sex').value; options: { plugins: { legend: { display: false } }, cutout: '70%' }
const act = parseFloat(document.getElementById('activity').value); });
const def = parseInt(document.getElementById('deficit').value);
// Mifflin-St Jeor Formula
let bmr = (s === 'male') ? (10 * w) + (6.25 * h) - (5 * a) + 5 : (10 * w) + (6.25 * h) - (5 * a) - 161;
let maintenance = Math.round(bmr * act);
let goal = maintenance + def;
document.getElementById('totalKcal').innerText = goal;
document.getElementById('maintVal').innerText = maintenance;
document.getElementById('curTag').innerText = w;
document.getElementById('tarTag').innerText = tw;
} }
async function sendToGroq() { function toggleUnits() {
isMetric = document.getElementById('metric').checked;
const w = document.getElementById('weight'), tw = document.getElementById('targetW'), h = document.getElementById('height');
if (isMetric) { w.value = Math.round(w.value / 2.205); tw.value = Math.round(tw.value / 2.205); h.value = Math.round(h.value * 2.54);
} else { w.value = Math.round(w.value * 2.205); tw.value = Math.round(tw.value * 2.205); h.value = Math.round(h.value / 2.54); }
document.getElementById('lblCurW').innerText = isMetric ? "Weight (kg)" : "Weight (lb)";
document.getElementById('lblH').innerText = isMetric ? "Height (cm)" : "Height (in)";
updateProfile();
}
function updateProfile() {
let w = parseFloat(document.getElementById('weight').value) || 0, h = parseFloat(document.getElementById('height').value) || 0;
let a = parseFloat(document.getElementById('age').value) || 0, act = parseFloat(document.getElementById('activity').value), agg = parseInt(document.getElementById('aggSlider').value);
let w_c = isMetric ? w : w / 2.205, h_c = isMetric ? h : h * 2.54;
let bmr = (10 * w_c) + (6.25 * h_c) - (5 * a) + 5;
let total = Math.round(bmr * act) + agg;
document.getElementById('totalKcal').innerText = total;
document.getElementById('aggValue').innerText = (agg > 0 ? '+' : '') + agg;
document.getElementById('aggStatus').innerText = agg < 0 ? "Weight Loss" : (agg > 0 ? "Bulking" : "Maintenance");
let pPct = agg < 0 ? 0.35 : 0.25, cPct = agg < 0 ? 0.35 : 0.50, fPct = 1 - (pPct + cPct);
let pG = Math.round((total * pPct) / 4), cG = Math.round((total * cPct) / 4), fG = Math.round((total * fPct) / 9);
document.getElementById('pGram').innerText = pG; document.getElementById('cGram').innerText = cG; document.getElementById('fGram').innerText = fG;
if(macroChart) { macroChart.data.datasets[0].data = [pG*4, cG*4, fG*9]; macroChart.update(); }
}
async function sendToGroq(customPrompt = null) {
const input = document.getElementById('userInput'); const input = document.getElementById('userInput');
const text = input.value.trim(); const text = customPrompt || input.value.trim();
if(!text) return; if(!text) return;
if(!customPrompt) addMsg('user', text);
if(!customPrompt) input.value = "";
const id = 'l-' + Date.now(); addMsg('ai', '<span class="spinner-border spinner-border-sm"></span> Calibrating...', id);
addMsg('user', text); const sysMsg = `Expert Nutritionist. Goal: ${document.getElementById('totalKcal').innerText} kcal. Macros: P:${document.getElementById('pGram').innerText}g, C:${document.getElementById('cGram').innerText}g, F:${document.getElementById('fGram').innerText}g. Use Markdown Tables.`;
input.value = "";
const loadId = 'load-' + Date.now();
addMsg('ai', '<span class="spinner-grow spinner-grow-sm"></span> Analyzing data...', loadId);
try { try {
const resp = await fetch("https://api.groq.com/openai/v1/chat/completions", { const resp = await fetch("https://api.groq.com/openai/v1/chat/completions", {
method: "POST", headers: { "Authorization": `Bearer ${GROQ_API_KEY}`, "Content-Type": "application/json" }, method: "POST", headers: { "Authorization": `Bearer ${GROQ_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({ model: "llama-3.3-70b-versatile", messages: [{ role: "system", content: sysMsg }, { role: "user", content: text }] })
model: "llama-3.3-70b-versatile",
messages: [
{ role: "system", content: `World-class Nutritionist. Goal: ${document.getElementById('totalKcal').innerText} kcal.
Target weight: ${document.getElementById('tarTag').innerText}kg.
STRICT RULE: Use Markdown TABLES for all meal plans and macro breakdowns. Use bold headers.` },
{ role: "user", content: text }
]
})
}); });
const data = await resp.json(); const d = await resp.json();
// Use marked.js to render professional tables document.getElementById(id).innerHTML = marked.parse(d.choices[0].message.content);
document.getElementById(loadId).innerHTML = marked.parse(data.choices[0].message.content); } catch(e) { document.getElementById(id).innerText = "Error connecting to AI."; }
} catch(e) { document.getElementById(loadId).innerText = "API Error!"; }
} }
function addMsg(type, content, id = null) { function generateShoppingList() {
sendToGroq("Based on the target calories and macros we discussed, generate a comprehensive shopping list for 7 days. Organize it by categories like Proteins, Vegetables, Grains, etc.");
}
function addMsg(t, c, i = null) {
const b = document.getElementById('chatBox'); const b = document.getElementById('chatBox');
b.innerHTML += `<div class="msg ${type} shadow-sm" ${id ? `id="${id}"` : ''}>${content}</div>`; b.innerHTML += `<div class="msg ${t} shadow-sm" ${i ? `id="${i}"` : ''}>${c}</div>`;
b.scrollTop = b.scrollHeight; b.scrollTop = b.scrollHeight;
} }
updateProfile();
initChart(); updateProfile();
</script> </script>
</body> </body>
</html> </html>