undefined / index.html
amirabbox's picture
i want this optimized for mobile without chnaging items, themes or anything else. just make it responsive for mobile phones
6a054bd verified
<script type="text/javascript">
var gk_isXlsx = false;
var gk_xlsxFileLookup = {};
var gk_fileData = {};
function filledCell(cell) {
return cell !== '' && cell != null;
}
function loadFileData(filename) {
if (gk_isXlsx && gk_xlsxFileLookup[filename]) {
try {
var workbook = XLSX.read(gk_fileData[filename], { type: 'base64' });
var firstSheetName = workbook.SheetNames[0];
var worksheet = workbook.Sheets[firstSheetName];
// Convert sheet to JSON to filter blank rows
var jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, blankrows: false, defval: '' });
// Filter out blank rows (rows where all cells are empty, null, or undefined)
var filteredData = jsonData.filter(row => row.some(filledCell));
// Heuristic to find the header row by ignoring rows with fewer filled cells than the next row
var headerRowIndex = filteredData.findIndex((row, index) =>
row.filter(filledCell).length >= filteredData[index + 1]?.filter(filledCell).length
);
// Fallback
if (headerRowIndex === -1 || headerRowIndex > 25) {
headerRowIndex = 0;
}
// Convert filtered JSON back to CSV
var csv = XLSX.utils.aoa_to_sheet(filteredData.slice(headerRowIndex)); // Create a new sheet from filtered array of arrays
csv = XLSX.utils.sheet_to_csv(csv, { header: 1 });
return csv;
} catch (e) {
console.error(e);
return "";
}
}
return gk_fileData[filename] || "";
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SnapCal AI - Real-time Food Analysis</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:wght@400;700&family=Noto+Sans+Devanagari:wght@400;700&family=Noto+Sans+Hebrew:wght@400;700&family=Noto+Sans+JP:wght@400;700&family=Noto+Sans+KR:wght@400;700&family=Noto+Sans+SC:wght@400;700&family=Noto+Sans+Thai:wght@400;700&family=Noto+Serif:wght@400;700&display=swap');
@media screen and (max-width: 767px) {
/* Mobile-specific styles */
.neu {
border-radius: 12px;
box-shadow: 5px 5px 10px #171717, -5px -5px 10px #252525;
}
body.light .neu {
box-shadow: 5px 5px 10px #D9D9D9, -5px -5px 10px #FFFFFF;
}
.neu-inset {
border-radius: 12px;
box-shadow: inset 5px 5px 10px #171717, inset -5px -5px 10px #252525;
}
body.light .neu-inset {
box-shadow: inset 5px 5px 10px #D9D9D9, inset -5px -5px 10px #FFFFFF;
}
}
body {
font-family: 'Noto Sans', 'Noto Sans Arabic', 'Noto Sans Devanagari', 'Noto Sans Hebrew', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans SC', 'Noto Sans Thai', sans-serif;
background: #1E1E1E;
color: #E0E0E0;
margin: 0;
padding: 0;
overflow-x: hidden;
transition: background 0.3s ease-in-out, color 0.3s ease-in-out;
text-align: center;
}
body.light {
background: #F0F0F0;
color: #333;
}
.neu {
background: #1E1E1E;
border-radius: 20px;
box-shadow: 10px 10px 20px #171717, -10px -10px 20px #252525;
transition: all 0.3s ease-in-out;
}
@media screen and (max-width: 767px) {
.neu {
border-radius: 12px;
}
}
body.light .neu {
background: #F0F0F0;
box-shadow: 10px 10px 20px #D9D9D9, -10px -10px 20px #FFFFFF;
}
.neu-inset {
background: #1E1E1E;
border-radius: 20px;
box-shadow: inset 10px 10px 20px #171717, inset -10px -10px 20px #252525;
}
@media screen and (max-width: 767px) {
.neu-inset {
border-radius: 12px;
}
}
body.light .neu-inset {
background: #F0F0F0;
box-shadow: inset 10px 10px 20px #D9D9D9, inset -10px -10px 20px #FFFFFF;
}
.neu-button {
background: #1E1E1E;
border: none;
border-radius: 50px;
padding: 12px 24px;
color: #E0E0E0;
font-weight: bold;
box-shadow: 5px 5px 10px #171717, -5px -5px 10px #252525;
transition: all 0.3s ease-in-out;
}
.neu-button:hover {
box-shadow: inset 5px 5px 10px #171717, inset -5px -5px 10px #252525;
}
body.light .neu-button {
background: #F0F0F0;
color: #333;
box-shadow: 5px 5px 10px #D9D9D9, -5px -5px 10px #FFFFFF;
}
body.light .neu-button:hover {
box-shadow: inset 5px 5px 10px #D9D9D9, inset -5px -5px 10px #FFFFFF;
}
.accent {
background: linear-gradient(45deg, #FF5722, #FF9800);
color: #FFF;
}
body.light .accent {
background: linear-gradient(45deg, #00E676, #4CAF50);
}
.camera-feed {
background: #000;
border: none;
position: relative;
overflow: hidden;
border-radius: 20px;
}
.scan-line {
position: absolute;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, #FF5722, transparent);
box-shadow: 0 0 10px #FF5722;
animation: scan 2s linear infinite;
z-index: 10;
}
body.light .scan-line {
background: linear-gradient(90deg, transparent, #00E676, transparent);
box-shadow: 0 0 10px #00E676;
}
@keyframes scan {
0% {
top: 0;
}
100% {
top: 100%;
}
}
.detection-item {
background: #1E1E1E;
border-radius: 12px;
padding: 8px;
z-index: 20;
text-align: center;
box-shadow: 5px 5px 10px #171717, -5px -5px 10px #252525;
}
body.light .detection-item {
background: #F0F0F0;
box-shadow: 5px 5px 10px #D9D9D9, -5px -5px 10px #FFFFFF;
}
.calorie-badge {
background: linear-gradient(45deg, #FF5722, #FF9800);
color: #FFF;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
}
body.light .calorie-badge {
background: linear-gradient(45deg, #00E676, #4CAF50);
}
.progress-bar {
height: 4px;
background: #252525;
border-radius: 2px;
overflow: hidden;
}
body.light .progress-bar {
background: #E0E0E0;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #FF5722, #FF9800);
width: 0%;
transition: width 0.3s ease-in-out;
}
body.light .progress-fill {
background: linear-gradient(90deg, #00E676, #4CAF50);
}
.component-highlight {
animation: pulse 1.5s infinite ease-in-out;
border: 2px solid #FF5722;
}
body.light .component-highlight {
border: 2px solid #00E676;
}
@keyframes pulse {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
.processing-overlay {
background: rgba(30, 30, 30, 0.9);
z-index: 30;
transition: opacity 0.5s ease-in-out;
}
body.light .processing-overlay {
background: rgba(240, 240, 240, 0.9);
}
.nav-button {
transition: all 0.6s ease-in-out;
position: relative;
}
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.8s ease-in-out, transform 0.8s ease-in-out;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
#cameraContainer {
transition: height 0.5s ease-in-out;
}
#analysisResults, #navButtons, #footer, #header {
transition: opacity 0.5s ease-in-out;
}
.nav-menu {
display: none;
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #1E1E1E;
border-radius: 12px;
padding: 8px;
min-width: 150px;
z-index: 40;
text-align: center;
box-shadow: 5px 5px 10px #171717, -5px -5px 10px #252525;
}
body.light .nav-menu {
background: #F0F0F0;
box-shadow: 5px 5px 10px #D9D9D9, -5px -5px 10px #FFFFFF;
}
.nav-button:hover .nav-menu {
display: block;
}
.nav-menu li {
padding: 4px 8px;
cursor: pointer;
transition: box-shadow 0.3s ease-in-out;
text-align: center;
border-radius: 8px;
}
.nav-menu li:hover {
box-shadow: inset 2px 2px 5px #171717, inset -2px -2px 5px #252525;
}
body.light .nav-menu li:hover {
box-shadow: inset 2px 2px 5px #D9D9D9, inset -2px -2px 5px #FFFFFF;
}
.modal {
display: none;
position: fixed;
z-index: 50;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
animation: fadeIn 0.3s ease-in-out;
transition: opacity 0.3s ease-in-out;
}
.modal.closing {
opacity: 0;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.modal-content {
background: #1E1E1E;
margin: 15% auto;
padding: 20px;
width: 80%;
max-width: 600px;
border-radius: 20px;
text-align: center;
animation: slideDown 0.3s ease-in-out;
transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
box-shadow: 10px 10px 20px #171717, -10px -10px 20px #252525;
}
.modal-content.closing {
transform: translateY(20px);
opacity: 0;
}
@keyframes slideDown {
from {
transform: translateY(-20px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
body.light .modal-content {
background: #F0F0F0;
box-shadow: 10px 10px 20px #D9D9D9, -10px -10px 20px #FFFFFF;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
transition: color 0.3s ease-in-out;
}
.close:hover,
.close:focus {
color: #FF5722;
text-decoration: none;
cursor: pointer;
}
.theme-toggle {
cursor: pointer;
transition: transform 0.3s ease-in-out;
}
.theme-toggle:hover {
transform: scale(1.1);
}
.border-black {
border-color: #000;
}
body.light .border-black {
border-color: #333;
}
.bg-white {
background: #1E1E1E;
}
body.light .bg-white {
background: #F0F0F0;
}
.text-gray-600 {
color: #A0A0A0;
}
body.light .text-gray-600 {
color: #666;
}
.text-gray-500 {
color: #808080;
}
body.light .text-gray-500 {
color: #999;
}
.hover\:bg-gray-100:hover {
background: #252525;
}
body.light .hover\:bg-gray-100:hover {
background: #E0E0E0;
}
.bg-black {
background: #252525;
}
body.light .bg-black {
background: #E0E0E0;
}
.text-white {
color: #E0E0E0;
}
body.light .text-white {
color: #333;
}
.text-black {
color: #E0E0E0;
}
body.light .text-black {
color: #333;
}
.upgrade-progress {
height: 10px;
background: #252525;
border-radius: 5px;
overflow: hidden;
margin-top: 10px;
}
.upgrade-progress-fill {
height: 100%;
background: linear-gradient(90deg, #FF5722, #FF9800);
width: 0%;
transition: width 1s ease-in-out;
}
body.light .upgrade-progress-fill {
background: linear-gradient(90deg, #00E676, #4CAF50);
}
.btn-primary {
background: #1E1E1E;
color: #E0E0E0;
padding: 10px 20px;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease-in-out;
box-shadow: 5px 5px 10px #171717, -5px -5px 10px #252525;
}
.btn-primary:hover {
box-shadow: inset 5px 5px 10px #171717, inset -5px -5px 10px #252525;
}
body.light .btn-primary {
background: #F0F0F0;
color: #333;
box-shadow: 5px 5px 10px #D9D9D9, -5px -5px 10px #FFFFFF;
}
body.light .btn-primary:hover {
box-shadow: inset 5px 5px 10px #D9D9D9, inset -5px -5px 10px #FFFFFF;
}
.success-message {
color: #FF9800;
margin-top: 10px;
animation: fadeInMessage 0.5s ease-in-out;
}
@keyframes fadeInMessage {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Advanced CSS for selects */
select {
appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg fill="%23E0E0E0" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
background-repeat: no-repeat;
background-position-x: calc(100% - 8px);
background-position-y: center;
padding-right: 2rem !important;
cursor: pointer;
transition: all 0.3s ease-in-out;
}
body.light select {
background-image: url('data:image/svg+xml;utf8,<svg fill="%23333" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
}
select:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(255, 87, 34, 0.5);
}
body.light select:focus {
box-shadow: 0 0 0 2px rgba(0, 230, 118, 0.5);
}
select option {
background-color: #1E1E1E;
color: #E0E0E0;
padding: 0.5rem;
}
body.light select option {
background-color: #F0F0F0;
color: #333;
}
/* RTL support */
body.rtl {
direction: rtl;
text-align: right;
}
body.rtl .flex {
flex-direction: row-reverse;
}
body.rtl .space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 1;
}
body.rtl .space-x-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 1;
}
body.rtl .justify-between {
flex-direction: row-reverse;
}
body.rtl .text-right {
text-align: left;
}
body.rtl .text-left {
text-align: right;
}
body.rtl .float-right {
float: left;
}
body.rtl .nav-menu {
text-align: right;
}
body.rtl select {
background-position-x: 8px;
padding-left: 2rem;
padding-right: 1rem;
}
body.rtl .absolute.right-2 {
right: auto;
left: 2rem;
}
</style>
</head>
<body class="min-h-screen">
<!-- Header -->
<header id="header" class="p-4 flex justify-between items-center neu hidden" style="opacity: 0;">
<div class="flex items-center space-x-2">
<i data-feather="camera" class="text-white"></i>
<h1 class="text-xl font-bold text-white">SnapCal AI</h1>
</div>
<div class="flex space-x-4 items-center">
<div class="relative flex items-center">
<select id="languageSelect" class="p-2 pr-8 rounded neu-inset text-white">
<option value="en">🇺🇸 English</option>
<option value="fa">🇮🇷 فارسی</option>
<option value="fr">🇫🇷 Français</option>
<option value="it">🇮🇹 Italiano</option>
<option value="de">🇩🇪 Deutsch</option>
</select>
<i data-feather="globe" class="absolute right-2 top-1/2 transform -translate-y-1/2 text-white pointer-events-none"></i>
</div>
<div id="themeToggle" class="p-2 rounded-full neu flex items-center justify-center theme-toggle">
<i data-feather="sun" class="text-white" id="themeIcon"></i>
</div>
<button class="p-2 rounded-full neu">
<i data-feather="user" class="text-white"></i>
</button>
</div>
</header>
<main class="container mx-auto px-4 py-8">
<div class="neu rounded-2xl overflow-hidden">
<!-- Camera Feed Container -->
<div id="cameraContainer" class="relative w-full h-screen camera-feed flex items-center justify-center">
<!-- Video Feed -->
<video id="videoFeed" class="absolute inset-0 w-full h-full object-cover" autoplay playsinline></video>
<!-- Placeholder (shown if no camera) -->
<div id="cameraPlaceholder" class="absolute inset-0 flex items-center justify-center z-5">
<div class="text-center">
<i data-feather="camera" class="w-16 h-16 mx-auto mb-4 text-white"></i>
<p class="text-lg text-white" data-lang-en="Live Camera Feed" data-lang-fa="فید دوربین زنده" data-lang-fr="Flux de caméra en direct" data-lang-it="Feed della telecamera dal vivo" data-lang-de="Live-Kamera-Feed">Live Camera Feed</p>
<p class="text-sm text-gray-300" data-lang-en="Point at your food to begin analysis" data-lang-fa="دوربین را به سمت غذای خود بگیرید تا تحلیل آغاز شود" data-lang-fr="Pointez la caméra vers votre nourriture pour commencer l'analyse" data-lang-it="Punta la fotocamera verso il cibo per iniziare l'analisi" data-lang-de="Richten Sie die Kamera auf Ihr Essen, um die Analyse zu starten">Point at your food to begin analysis</p>
</div>
</div>
<!-- Scan Line Effect -->
<div class="scan-line"></div>
<!-- Processing Overlay -->
<div id="processingOverlay" class="processing-overlay absolute inset-0 hidden flex-col items-center justify-center">
<div class="text-center mb-4 md:mb-6 z-20">
<div class="w-12 h-12 md:w-16 md:h-16 border-4 border-white border-t-transparent rounded-full animate-spin mx-auto mb-2 md:mb-4"></div>
<h3 class="text-xl font-bold" data-lang-en="Analyzing Food..." data-lang-fa="در حال تحلیل غذا..." data-lang-fr="Analyse de la nourriture..." data-lang-it="Analisi del cibo in corso..." data-lang-de="Lebensmittel werden analysiert...">Analyzing Food...</h3>
<p class="text-gray-600" data-lang-en="Processing image to detect components" data-lang-fa="پردازش تصویر برای تشخیص اجزا" data-lang-fr="Traitement de l'image pour détecter les composants" data-lang-it="Elaborazione dell'immagine per rilevare i componenti" data-lang-de="Verarbeitung des Bildes zur Erkennung der Bestandteile">Processing image to detect components</p>
</div>
<div class="w-48 md:w-64 progress-bar">
<div id="progressFill" class="progress-fill"></div>
</div>
</div>
<!-- Detection Results -->
<div id="detectionResults" class="absolute inset-0 hidden flex items-center justify-center">
<div class="text-center z-20">
<div class="absolute top-1/3 left-1/4 w-16 h-16 md:w-24 md:h-24 border-2 border-white rounded-lg component-highlight"></div>
<div class="absolute bottom-1/3 right-1/3 w-14 h-14 md:w-20 md:h-20 border-2 border-white rounded-lg component-highlight"></div>
<div class="absolute top-1/3 left-1/4 transform -translate-x-1/2 -translate-y-1/2">
<div class="detection-item flex items-center space-x-2 neu-inset">
<span class="font-semibold" data-lang-en="Pancake" data-lang-fa="پنکیک" data-lang-fr="Crêpe" data-lang-it="Pancake" data-lang-de="Pfannkuchen">Pancake</span>
<span class="calorie-badge">220 cal</span>
</div>
</div>
<div class="absolute bottom-1/3 right-1/3 transform translate-x-1/2 translate-y-1/2">
<div class="detection-item flex items-center space-x-2 neu-inset">
<span class="font-semibold" data-lang-en="Maple Syrup" data-lang-fa="شربت افرا" data-lang-fr="Sirop d'érable" data-lang-it="Sciroppo d'acero" data-lang-de="Ahornsirup">Maple Syrup</span>
<span class="calorie-badge">52 cal</span>
</div>
</div>
</div>
</div>
</div>
<!-- Analysis Results -->
<div id="analysisResults" class="p-6 hidden" style="opacity: 0;">
<div class="flex justify-between items-center mb-4 md:mb-6">
<h2 class="text-xl md:text-2xl font-bold" data-lang-en="Food Analysis" data-lang-fa="تحلیل غذا" data-lang-fr="Analyse alimentaire" data-lang-it="Analisi del cibo" data-lang-de="Lebensmittelanalyse">Food Analysis</h2>
<div class="flex items-center space-x-2">
<span class="px-3 py-1 rounded-full text-sm font-medium neu-inset">Live</span>
<span class="text-sm text-gray-500" data-lang-en="Updated just now" data-lang-fa="به‌روزرسانی شده همین حالا" data-lang-fr="Mis à jour à l'instant" data-lang-it="Aggiornato proprio ora" data-lang-de="Gerade jetzt aktualisiert">Updated just now</span>
</div>
</div>
<!-- Total Calories -->
<div class="neu rounded-2xl p-4 md:p-6 mb-4 md:mb-6">
<div class="flex justify-between items-center">
<div>
<p class="text-lg text-gray-600" data-lang-en="Total Calories" data-lang-fa="کالری کل" data-lang-fr="Calories totales" data-lang-it="Calorie totali" data-lang-de="Gesamtkalorien">Total Calories</p>
<p class="text-3xl md:text-4xl font-bold">272 kcal</p>
</div>
<div class="text-right">
<p class="text-lg text-gray-600" data-lang-en="Components" data-lang-fa="اجزا" data-lang-fr="Composants" data-lang-it="Componenti" data-lang-de="Komponenten">Components</p>
<p class="text-3xl md:text-4xl font-bold">2</p>
</div>
</div>
</div>
<!-- Detected Components -->
<div class="space-y-4">
<h3 class="text-lg font-semibold" data-lang-en="Detected Components" data-lang-fa="اجزای تشخیص داده شده" data-lang-fr="Composants détectés" data-lang-it="Componenti rilevati" data-lang-de="Erkannte Komponenten">Detected Components</h3>
<div class="flex items-center justify-between p-4 rounded-xl neu">
<div class="flex items-center space-x-4">
<div class="w-10 h-10 md:w-12 md:h-12 rounded-lg flex items-center justify-center neu-inset">
<i data-feather="circle" class="text-white"></i>
</div>
<div>
<h4 class="font-medium" data-lang-en="Pancake" data-lang-fa="پنکیک" data-lang-fr="Crêpe" data-lang-it="Pancake" data-lang-de="Pfannkuchen">Pancake</h4>
<p class="text-sm text-gray-600" data-lang-en="Flour, egg, milk, butter" data-lang-fa="آرد، تخم‌مرغ، شیر، کره" data-lang-fr="Farine, œuf, lait, beurre" data-lang-it="Farina, uovo, latte, burro" data-lang-de="Mehl, Ei, Milch, Butter">Flour, egg, milk, butter</p>
</div>
</div>
<div class="text-right">
<p class="font-semibold">220 kcal</p>
<p class="text-sm text-gray-600" data-lang-en="1 serving" data-lang-fa="1 وعده" data-lang-fr="1 portion" data-lang-it="1 porzione" data-lang-de="1 Portion">1 serving</p>
</div>
</div>
<div class="flex items-center justify-between p-4 rounded-xl neu">
<div class="flex items-center space-x-4">
<div class="w-12 h-12 rounded-lg flex items-center justify-center neu-inset">
<i data-feather="droplet" class="text-white"></i>
</div>
<div>
<h4 class="font-medium" data-lang-en="Maple Syrup" data-lang-fa="شربت افرا" data-lang-fr="Sirop d'érable" data-lang-it="Sciroppo d'acero" data-lang-de="Ahornsirup">Maple Syrup</h4>
<p class="text-sm text-gray-600" data-lang-en="Natural sweetener" data-lang-fa="شیرین‌کننده طبیعی" data-lang-fr="Édulcorant naturel" data-lang-it="Edulcorante naturale" data-lang-de="Natürlicher Süßstoff">Natural sweetener</p>
</div>
</div>
<div class="text-right">
<p class="font-semibold">52 kcal</p>
<p class="text-sm text-gray-600" data-lang-en="1 tbsp" data-lang-fa="1 قاشق غذاخوری" data-lang-fr="1 cuillère à soupe" data-lang-it="1 cucchiaio" data-lang-de="1 Esslöffel">1 tbsp</p>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="mt-8 flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
<button id="saveMealButton" class="flex-1 neu-button accent flex items-center justify-center space-x-2">
<i data-feather="save"></i>
<span data-lang-en="Save Meal" data-lang-fa="ذخیره وعده غذایی" data-lang-fr="Enregistrer le repas" data-lang-it="Salva pasto" data-lang-de="Mahlzeit speichern">Save Meal</span>
</button>
<button id="rescanButton" class="flex-1 neu-button flex items-center justify-center space-x-2">
<i data-feather="refresh-ccw"></i>
<span data-lang-en="Rescan" data-lang-fa="اسکن مجدد" data-lang-fr="Rescanner" data-lang-it="Riscansiona" data-lang-de="Erneut scannen">Rescan</span>
</button>
</div>
</div>
</div>
</main>
<!-- Navigation Buttons -->
<div id="navButtons" class="fixed bottom-0 left-0 right-0 p-2 md:p-4 hidden neu" style="opacity: 0;">
<div class="flex justify-around items-center">
<button class="nav-button flex flex-col items-center space-y-1 fade-in neu-button">
<i data-feather="home" class="w-5 h-5 md:w-6 md:h-6"></i>
<span class="text-xs" data-lang-en="Home" data-lang-fa="خانه" data-lang-fr="Accueil" data-lang-it="Home" data-lang-de="Startseite">Home</span>
</button>
<div class="nav-button flex flex-col items-center space-y-1 fade-in relative neu-button">
<i data-feather="book" class="w-6 h-6"></i>
<span class="text-xs" data-lang-en="Dishes" data-lang-fa="غذاها" data-lang-fr="Plats" data-lang-it="Piatti" data-lang-de="Gerichte">Dishes</span>
<ul class="nav-menu">
<li data-lang-en="Soup" data-lang-fa="سوپ" data-lang-fr="Soupe" data-lang-it="Zuppa" data-lang-de="Suppe">Soup</li>
<li data-lang-en="Sandwich" data-lang-fa="ساندویچ" data-lang-fr="Sandwich" data-lang-it="Panino" data-lang-de="Sandwich">Sandwich</li>
<li data-lang-en="Khoresht" data-lang-fa="خورش" data-lang-fr="Khoresht" data-lang-it="Khoresht" data-lang-de="Khoresht">Khoresht</li>
<li data-lang-en="Polo" data-lang-fa="پلو" data-lang-fr="Polo" data-lang-it="Polo" data-lang-de="Polo">Polo</li>
<li data-lang-en="Salad" data-lang-fa="سالاد" data-lang-fr="Salade" data-lang-it="Insalata" data-lang-de="Salat">Salad</li>
<li data-lang-en="Dessert" data-lang-fa="دسر" data-lang-fr="Dessert" data-lang-it="Dessert" data-lang-de="Dessert">Dessert</li>
</ul>
</div>
<div class="nav-button flex flex-col items-center space-y-1 fade-in relative neu-button">
<i data-feather="user" class="w-6 h-6"></i>
<span class="text-xs" data-lang-en="Account" data-lang-fa="حساب کاربری" data-lang-fr="Compte" data-lang-it="Account" data-lang-de="Konto">Account</span>
<ul class="nav-menu">
<li onclick="openModal('profileModal')" data-lang-en="Profile" data-lang-fa="پروفایل" data-lang-fr="Profil" data-lang-it="Profilo" data-lang-de="Profil">Profile</li>
<li onclick="openModal('premiumModal')" data-lang-en="Upgrade to Premium" data-lang-fa="ارتقا به پرمیوم" data-lang-fr="Passer à Premium" data-lang-it="Passa a Premium" data-lang-de="Auf Premium upgraden">Upgrade to Premium</li>
<li onclick="openModal('historyModal')" data-lang-en="Meal History" data-lang-fa="تاریخچه وعده‌ها" data-lang-fr="Historique des repas" data-lang-it="Cronologia pasti" data-lang-de="Mahlzeitenverlauf">Meal History</li>
<li onclick="openModal('accountSettingsModal')" data-lang-en="Settings" data-lang-fa="تنظیمات" data-lang-fr="Paramètres" data-lang-it="Impostazioni" data-lang-de="Einstellungen">Settings</li>
<li onclick="confirmLogout()" data-lang-en="Logout" data-lang-fa="خروج" data-lang-fr="Déconnexion" data-lang-it="Disconnessione" data-lang-de="Abmelden">Logout</li>
</ul>
</div>
<div class="nav-button flex flex-col items-center space-y-1 fade-in relative neu-button">
<i data-feather="settings" class="w-6 h-6"></i>
<span class="text-xs" data-lang-en="Settings" data-lang-fa="تنظیمات" data-lang-fr="Paramètres" data-lang-it="Impostazioni" data-lang-de="Einstellungen">Settings</span>
<ul class="nav-menu">
<li onclick="openModal('languageModal')" data-lang-en="Language" data-lang-fa="زبان" data-lang-fr="Langue" data-lang-it="Lingua" data-lang-de="Sprache">Language</li>
<li onclick="openModal('notificationsModal')" data-lang-en="Notifications" data-lang-fa="اعلان‌ها" data-lang-fr="Notifications" data-lang-it="Notifiche" data-lang-de="Benachrichtigungen">Notifications</li>
<li onclick="openModal('privacyModal')" data-lang-en="Privacy" data-lang-fa="حریم خصوصی" data-lang-fr="Confidentialité" data-lang-it="Privacy" data-lang-de="Datenschutz">Privacy</li>
<li onclick="openModal('supportModal')" data-lang-en="Help & Support" data-lang-fa="کمک و پشتیبانی" data-lang-fr="Aide & Support" data-lang-it="Aiuto & Supporto" data-lang-de="Hilfe & Support">Help & Support</li>
<li onclick="openModal('aboutModal')" data-lang-en="About" data-lang-fa="درباره ما" data-lang-fr="À propos" data-lang-it="Informazioni" data-lang-de="Über">About</li>
</ul>
</div>
</div>
</div>
<!-- Footer -->
<footer id="footer" class="py-8 text-center text-gray-600 hidden neu" style="opacity: 0;">
<p data-lang-en="© 2023 SnapCal AI by Team Nora. All rights reserved." data-lang-fa="© ۲۰۲۳ SnapCal AI توسط تیم نورا. همه حقوق محفوظ است." data-lang-fr="© 2023 SnapCal AI par Team Nora. Tous droits réservés." data-lang-it="© 2023 SnapCal AI da Team Nora. Tutti i diritti riservati." data-lang-de="© 2023 SnapCal AI von Team Nora. Alle Rechte vorbehalten.">© 2023 SnapCal AI by Team Nora. All rights reserved.</p>
<p data-lang-en="Trademark of Team Nora. Rights reserved." data-lang-fa="علامت تجاری تیم نورا. حقوق محفوظ." data-lang-fr="Marque de Team Nora. Droits réservés." data-lang-it="Marchio di Team Nora. Diritti riservati." data-lang-de="Marke von Team Nora. Rechte vorbehalten.">Trademark of Team Nora. Rights reserved.</p>
</footer>
<!-- Modals -->
<div id="aboutModal" class="modal">
<div class="modal-content neu" style="width: 90%; margin: 10% auto;">
<span class="close" onclick="closeModal('aboutModal')">&times;</span>
<h2 data-lang-en="About Us" data-lang-fa="درباره ما" data-lang-fr="À propos de nous" data-lang-it="Chi siamo" data-lang-de="Über uns">About Us</h2>
<p data-lang-en="SnapCal AI is developed by Team Nora, a group of passionate developers focused on making healthy eating easier through AI technology." data-lang-fa="SnapCal AI توسط تیم نورا توسعه یافته است، گروهی از توسعه‌دهندگان مشتاق که بر روی آسان کردن خوردن سالم از طریق فناوری هوش مصنوعی تمرکز دارند." data-lang-fr="SnapCal AI est développé par Team Nora, un groupe de développeurs passionnés axés sur la simplification de l'alimentation saine grâce à la technologie IA." data-lang-it="SnapCal AI è sviluppato da Team Nora, un gruppo di sviluppatori appassionati focalizzati sul rendere più facile una dieta sana tramite la tecnologia AI." data-lang-de="SnapCal AI wird von Team Nora entwickelt, einer Gruppe leidenschaftlicher Entwickler, die sich darauf konzentrieren, gesundes Essen durch KI-Technologie zu erleichtern.">SnapCal AI is developed by Team Nora, a group of passionate developers focused on making healthy eating easier through AI technology.</p>
</div>
</div>
<div id="supportModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('supportModal')">&times;</span>
<h2 data-lang-en="Help & Support" data-lang-fa="کمک و پشتیبانی" data-lang-fr="Aide & Support" data-lang-it="Aiuto & Supporto" data-lang-de="Hilfe & Support">Help & Support</h2>
<p data-lang-en="Contact us at support@teamnora.com or call +1-123-456-7890 for assistance." data-lang-fa="با ما تماس بگیرید در support@teamnora.com یا شماره +۱-۱۲۳-۴۵۶-۷۸۹۰ برای کمک." data-lang-fr="Contactez-nous à support@teamnora.com ou appelez le +1-123-456-7890 pour de l'aide." data-lang-it="Contattaci a support@teamnora.com o chiama il +1-123-456-7890 per assistenza." data-lang-de="Kontaktiere uns unter support@teamnora.com oder ruf +1-123-456-7890 an für Hilfe.">Contact us at support@teamnora.com or call +1-123-456-7890 for assistance.</p>
<button class="neu-button mt-4" onclick="sendSupportEmail()">Send Email</button>
<p id="supportStatus" class="success-message hidden">Email sent successfully!</p>
</div>
</div>
<div id="privacyModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('privacyModal')">&times;</span>
<h2 data-lang-en="Privacy Policy" data-lang-fa="سیاست حریم خصوصی" data-lang-fr="Politique de confidentialité" data-lang-it="Politica sulla privacy" data-lang-de="Datenschutzrichtlinie">Privacy Policy</h2>
<p data-lang-en="We value your privacy. Your data is secure and not shared without consent. For more details, read our full policy." data-lang-fa="ما به حریم خصوصی شما اهمیت می‌دهیم. داده‌های شما امن است و بدون رضایت به اشتراک گذاشته نمی‌شود. برای جزئیات بیشتر، سیاست کامل ما را بخوانید." data-lang-fr="Nous valorisons votre vie privée. Vos données sont sécurisées et ne sont pas partagées sans consentement. Pour plus de détails, lisez notre politique complète." data-lang-it="Valorizziamo la tua privacy. I tuoi dati sono sicuri e non vengono condivisi senza consenso. Per maggiori dettagli, leggi la nostra politica completa." data-lang-de="Wir schätzen Ihre Privatsphäre. Ihre Daten sind sicher und werden ohne Zustimmung nicht geteilt. Lesen Sie unsere vollständige Richtlinie für weitere Details.">We value your privacy. Your data is secure and not shared without consent. For more details, read our full policy.</p>
</div>
</div>
<div id="notificationsModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('notificationsModal')">&times;</span>
<h2 data-lang-en="Notifications Settings" data-lang-fa="تنظیمات اعلان‌ها" data-lang-fr="Paramètres des notifications" data-lang-it="Impostazioni notifiche" data-lang-de="Benachrichtigungseinstellungen">Notifications Settings</h2>
<label>
<input type="checkbox" id="silentMode" onchange="updateNotificationSettings()">
<span data-lang-en="Silent Mode" data-lang-fa="حالت سایلنت" data-lang-fr="Mode silencieux" data-lang-it="Modalità silenziosa" data-lang-de="Stiller Modus">Silent Mode</span>
</label>
<br>
<label>
<input type="checkbox" id="dailyReminders" onchange="updateNotificationSettings()">
<span data-lang-en="Daily Reminders" data-lang-fa="یادآوری روزانه" data-lang-fr="Rappels quotidiens" data-lang-it="Promemoria giornalieri" data-lang-de="Tägliche Erinnerungen">Daily Reminders</span>
</label>
<p id="notificationStatus" class="text-gray-600 mt-2 success-message"></p>
</div>
</div>
<div id="profileModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('profileModal')">&times;</span>
<h2 data-lang-en="Profile" data-lang-fa="پروفایل" data-lang-fr="Profil" data-lang-it="Profilo" data-lang-de="Profil">Profile</h2>
<form id="profileForm" onsubmit="saveProfile(event)">
<label data-lang-en="Name:" data-lang-fa="نام:" data-lang-fr="Nom:" data-lang-it="Nome:" data-lang-de="Name:">Name:</label>
<input type="text" id="profileName" class="p-2 mt-2 rounded neu-inset bg-transparent text-white w-full" placeholder="Enter your name">
<br><br>
<label data-lang-en="Email:" data-lang-fa="ایمیل:" data-lang-fr="Email:" data-lang-it="Email:" data-lang-de="E-Mail:">Email:</label>
<input type="email" id="profileEmail" class="p-2 mt-2 rounded neu-inset bg-transparent text-white w-full" placeholder="Enter your email">
<br><br>
<button type="submit" class="neu-button mt-4">Save</button>
</form>
<p id="profileStatus" class="success-message hidden">Profile saved successfully!</p>
</div>
</div>
<div id="premiumModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('premiumModal')">&times;</span>
<h2 data-lang-en="Upgrade to Premium" data-lang-fa="ارتقا به پرمیوم" data-lang-fr="Passer à Premium" data-lang-it="Passa a Premium" data-lang-de="Auf Premium upgraden">Upgrade to Premium</h2>
<p data-lang-en="Get unlimited scans and more features." data-lang-fa="اسکن‌های نامحدود و ویژگی‌های بیشتر دریافت کنید." data-lang-fr="Obtenez des scans illimités et plus de fonctionnalités." data-lang-it="Ottieni scansioni illimitate e più funzionalità." data-lang-de="Erhalte unbegrenzte Scans und mehr Funktionen.">Get unlimited scans and more features.</p>
<button onclick="upgradeToPremium()" class="neu-button accent mt-4">Upgrade</button>
<div id="upgradeProgress" class="upgrade-progress mt-2 hidden">
<div id="upgradeProgressFill" class="upgrade-progress-fill"></div>
</div>
<p id="upgradeStatus" class="success-message mt-2"></p>
</div>
</div>
<div id="historyModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('historyModal')">&times;</span>
<h2 data-lang-en="Meal History" data-lang-fa="تاریخچه وعده‌ها" data-lang-fr="Historique des repas" data-lang-it="Cronologia pasti" data-lang-de="Mahlzeitenverlauf">Meal History</h2>
<ul id="mealHistoryList" class="text-left mt-4 space-y-2">
<li class="p-2 neu-inset">2023-10-01: Salad (150 kcal)</li>
<li class="p-2 neu-inset">2023-10-02: Sandwich (300 kcal)</li>
</ul>
<button onclick="clearHistory()" class="neu-button mt-4">Clear History</button>
<p id="historyStatus" class="success-message hidden">History cleared!</p>
</div>
</div>
<div id="accountSettingsModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('accountSettingsModal')">&times;</span>
<h2 data-lang-en="Account Settings" data-lang-fa="تنظیمات حساب" data-lang-fr="Paramètres du compte" data-lang-it="Impostazioni account" data-lang-de="Kontoeinstellungen">Account Settings</h2>
<p data-lang-en="Manage your account settings." data-lang-fa="مدیریت تنظیمات حساب شما." data-lang-fr="Gérez les paramètres de votre compte." data-lang-it="Gestisci le impostazioni del tuo account." data-lang-de="Verwalten Sie Ihre Kontoeinstellungen.">Manage your account settings.</p>
<button onclick="deleteAccount()" class="neu-button mt-4 accent bg-red-500">Delete Account</button>
<p id="accountStatus" class="success-message hidden">Account deleted!</p>
</div>
</div>
<div id="languageModal" class="modal">
<div class="modal-content neu">
<span class="close" onclick="closeModal('languageModal')">&times;</span>
<h2 data-lang-en="Language Settings" data-lang-fa="تنظیمات زبان" data-lang-fr="Paramètres de langue" data-lang-it="Impostazioni lingua" data-lang-de="Spracheinstellungen">Language Settings</h2>
<select id="modalLanguageSelect" class="p-2 rounded neu-inset bg-transparent text-white">
<option value="en">🇺🇸 English</option>
<option value="fa">🇮🇷 فارسی</option>
<option value="fr">🇫🇷 Français</option>
<option value="it">🇮🇹 Italiano</option>
<option value="de">🇩🇪 Deutsch</option>
</select>
<button onclick="changeLanguage(document.getElementById('modalLanguageSelect').value)" class="neu-button mt-4">Apply</button>
<p id="languageStatus" class="success-message hidden">Language changed!</p>
</div>
</div>
<script>
feather.replace();
// Initialize camera stream
async function initCamera() {
const video = document.getElementById('videoFeed');
const placeholder = document.getElementById('cameraPlaceholder');
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });
video.srcObject = stream;
video.play();
placeholder.style.display = 'none';
} catch (error) {
console.error('Error accessing camera:', error);
// Fallback to user-facing if environment fails
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
video.play();
placeholder.style.display = 'none';
} catch (fallbackError) {
console.error('Fallback error:', fallbackError);
// Show placeholder or alert
alert('Unable to access camera. Please check permissions.');
}
}
} else {
alert('getUserMedia is not supported in this browser.');
}
}
// Theme Toggle
const themeToggle = document.getElementById('themeToggle');
const themeIcon = document.getElementById('themeIcon');
themeToggle.addEventListener('click', () => {
document.body.classList.toggle('light');
if (document.body.classList.contains('light')) {
themeIcon.setAttribute('data-feather', 'moon');
} else {
themeIcon.setAttribute('data-feather', 'sun');
}
feather.replace();
});
// Language Change
function changeLanguage(lang) {
document.querySelectorAll('[data-lang-en]').forEach(el => {
el.textContent = el.getAttribute(`data-lang-${lang}`);
});
if (lang === 'fa') {
document.body.classList.add('rtl');
document.documentElement.dir = 'rtl';
} else {
document.body.classList.remove('rtl');
document.documentElement.dir = 'ltr';
}
}
const languageSelect = document.getElementById('languageSelect');
languageSelect.addEventListener('change', () => {
changeLanguage(languageSelect.value);
});
// Modals
function openModal(modalId) {
const modal = document.getElementById(modalId);
modal.style.display = 'block';
modal.focus(); // For accessibility
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
const modalContent = modal.querySelector('.modal-content');
modal.classList.add('closing');
modalContent.classList.add('closing');
setTimeout(() => {
modal.style.display = 'none';
modal.classList.remove('closing');
modalContent.classList.remove('closing');
}, 300);
}
// Close modals when clicking outside
window.onclick = function (event) {
if (event.target.classList.contains('modal')) {
closeModal(event.target.id);
}
}
// Close with Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.modal').forEach(modal => {
if (modal.style.display === 'block') {
closeModal(modal.id);
}
});
}
});
// Interactive functions
function confirmLogout() {
if (confirm("Are you sure you want to logout?")) {
alert("Logged out successfully!");
// Actual logout logic here
}
}
function updateNotificationSettings() {
const status = document.getElementById('notificationStatus');
status.textContent = "Settings updated!";
status.classList.remove('hidden');
setTimeout(() => {
status.classList.add('hidden');
}, 2000);
}
function saveProfile(event) {
event.preventDefault();
const status = document.getElementById('profileStatus');
status.classList.remove('hidden');
setTimeout(() => {
status.classList.add('hidden');
}, 2000);
// Save logic here
}
function upgradeToPremium() {
const progress = document.getElementById('upgradeProgress');
const fill = document.getElementById('upgradeProgressFill');
const status = document.getElementById('upgradeStatus');
progress.classList.remove('hidden');
fill.style.width = '100%';
setTimeout(() => {
status.textContent = "Upgrade complete!";
progress.classList.add('hidden');
fill.style.width = '0%';
}, 1000);
}
function clearHistory() {
const list = document.getElementById('mealHistoryList');
const status = document.getElementById('historyStatus');
list.innerHTML = '';
status.classList.remove('hidden');
setTimeout(() => {
status.classList.add('hidden');
}, 2000);
}
function deleteAccount() {
if (confirm("Are you sure you want to delete your account? This action cannot be undone.")) {
const status = document.getElementById('accountStatus');
status.classList.remove('hidden');
setTimeout(() => {
closeModal('accountSettingsModal');
}, 2000);
// Delete logic here
}
}
function sendSupportEmail() {
const status = document.getElementById('supportStatus');
status.classList.remove('hidden');
setTimeout(() => {
status.classList.add('hidden');
}, 2000);
// Email logic here
}
// Scan simulation function
function startScan() {
const cameraContainer = document.getElementById('cameraContainer');
const processingOverlay = document.getElementById('processingOverlay');
const detectionResults = document.getElementById('detectionResults');
const progressFill = document.getElementById('progressFill');
const header = document.getElementById('header');
const analysisResults = document.getElementById('analysisResults');
const navButtons = document.getElementById('navButtons');
const footer = document.getElementById('footer');
const navItems = document.querySelectorAll('.fade-in');
// Reset UI to scanning state
cameraContainer.style.height = '100vh';
detectionResults.classList.add('hidden');
analysisResults.style.opacity = '0';
analysisResults.classList.add('hidden');
header.style.opacity = '0';
header.classList.add('hidden');
navButtons.style.opacity = '0';
navButtons.classList.add('hidden');
footer.style.opacity = '0';
footer.classList.add('hidden');
setTimeout(() => {
processingOverlay.classList.remove('hidden');
processingOverlay.classList.add('flex');
processingOverlay.style.opacity = '1';
let progress = 0;
const progressInterval = setInterval(() => {
progress += Math.random() * 15;
if (progress >= 100) {
progress = 100;
clearInterval(progressInterval);
setTimeout(() => {
processingOverlay.style.opacity = '0';
setTimeout(() => {
processingOverlay.classList.add('hidden');
processingOverlay.classList.remove('flex');
}, 500);
detectionResults.classList.remove('hidden');
cameraContainer.style.height = '50vh';
header.classList.remove('hidden');
header.style.opacity = '1';
analysisResults.classList.remove('hidden');
analysisResults.style.opacity = '1';
navButtons.classList.remove('hidden');
navButtons.style.opacity = '1';
footer.classList.remove('hidden');
footer.style.opacity = '1';
setTimeout(() => {
navItems.forEach((item, index) => {
setTimeout(() => {
item.classList.add('visible');
}, index * 200);
});
}, 500);
}, 500);
}
progressFill.style.width = progress + '%';
}, 100);
}, 1000); // Delay to allow smooth transition
}
// Simulate detection process on load
document.addEventListener('DOMContentLoaded', function () {
initCamera();
startScan();
});
// Rescan button
document.getElementById('rescanButton').addEventListener('click', startScan);
// Save Meal button (placeholder)
document.getElementById('saveMealButton').addEventListener('click', () => {
alert('Meal saved successfully!');
// Actual save logic here
});
</script>
</body>
</html>