added things
This commit is contained in:
parent
0c10365da1
commit
880c3377f3
31
app.js
31
app.js
@ -7,44 +7,13 @@ const port = 3000;
|
||||
|
||||
const upload = multer({ dest: 'uploads/' });
|
||||
|
||||
const GROQ_API_KEY = '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';
|
||||
const model = 'llama3-8b-8192';
|
||||
|
||||
app.use(express.static("static"));
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(__dirname + '/static/index.html');
|
||||
});
|
||||
|
||||
|
||||
app.post('/completions', async (req, res) => {
|
||||
try {
|
||||
const query = req.body.query;
|
||||
if (!query) {
|
||||
return res.status(400).json({ error: 'Prompt is required' });
|
||||
}
|
||||
|
||||
const data = {
|
||||
messages: [{ role: 'user', content: query }],
|
||||
model,
|
||||
};
|
||||
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization: `Bearer ${GROQ_API_KEY}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
|
||||
const response = await axios.post('https://api.groq.com/openai/v1/chat/completions', data, config);
|
||||
res.json(response.data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Error completing prompt' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/upload', upload.single('image'), async (req, res) => {
|
||||
try {
|
||||
const image = fs.readFileSync(req.file.path, { encoding: 'base64' });
|
||||
|
1336
package-lock.json
generated
1336
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,5 +3,11 @@
|
||||
"axios": "^1.6.8",
|
||||
"express": "^4.19.2",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app.js"
|
||||
}
|
||||
}
|
||||
|
1
static/dist.css
Normal file
1
static/dist.css
Normal file
File diff suppressed because one or more lines are too long
BIN
static/header.png
Normal file
BIN
static/header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 241 KiB |
@ -1,162 +1,176 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- <!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Upload Image</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="file" id="imageInput" accept="image/*" required=""> <button onclick="uploadImage()">Upload Image</button>
|
||||
<br><br>
|
||||
<input type="file" id="imageInput" accept="image/*" required />
|
||||
<button onclick="uploadImage()">Upload Image</button> <br /><br />
|
||||
<div id="editable-classes"></div>
|
||||
<button id="add-class-btn">Add Class</button>
|
||||
<div id="suggested-classes"></div>
|
||||
<br>
|
||||
</body>
|
||||
</html> -->
|
||||
|
||||
<button onclick="generateIngredients()">Generate Ingredients</button>
|
||||
<script>
|
||||
const editableClassesDiv = document.getElementById("editable-classes");
|
||||
const suggestedClassesDiv = document.getElementById("suggested-classes");
|
||||
const addClassBtn = document.getElementById("add-class-btn");
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>Fridge2Fit</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Fridge2Fit is an AI-powered recipe generator that helps individuals with obesity take control of their diet. Simply upload a picture of your fridge contents, and our algorithm will suggest healthy recipes tailored to your ingredients. Our goal is to empower users to make informed food choices, reduce food waste, and promote a healthier lifestyle."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="ai artificial intelligence fridge fit food fat obesity health healthy"
|
||||
/>
|
||||
|
||||
const uploadImage = async () => {
|
||||
const input = document.getElementById('imageInput');
|
||||
const file = input.files[0];
|
||||
const formData = new FormData();
|
||||
formData.append('image', file);
|
||||
<link href="/dist.css" rel="stylesheet" />
|
||||
<script src="/load.js" defer></script>
|
||||
</head>
|
||||
|
||||
try {
|
||||
const response = await fetch('../upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
<body
|
||||
class="leading-normal tracking-normal text-indigo-400 m-6 bg-cover bg-fixed overflow-hidden"
|
||||
style="background-image: url('header.png')"
|
||||
>
|
||||
<div class="h-full">
|
||||
<!--Nav-->
|
||||
<div class="w-full container mx-auto">
|
||||
<div class="w-full flex items-center justify-between">
|
||||
<a
|
||||
class="flex items-center text-indigo-400 no-underline hover:no-underline font-bold text-2xl lg:text-4xl"
|
||||
href="#"
|
||||
>
|
||||
Fridge<span
|
||||
class="bg-clip-text text-transparent bg-gradient-to-r from-green-400 via-pink-500 to-purple-500"
|
||||
>2Fit</span
|
||||
>
|
||||
</a>
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to upload image');
|
||||
}
|
||||
<div class="flex w-1/2 justify-end content-center">
|
||||
<a
|
||||
class="inline-block text-blue-300 no-underline hover:text-pink-500 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 transform hover:scale-125 duration-300 ease-in-out"
|
||||
href="https://twitter.com/intent/tweet?url=#"
|
||||
>
|
||||
<svg
|
||||
class="fill-current h-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
class="inline-block text-blue-300 no-underline hover:text-pink-500 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 transform hover:scale-125 duration-300 ease-in-out"
|
||||
href="https://www.facebook.com/sharer/sharer.php?u=#"
|
||||
>
|
||||
<svg
|
||||
class="fill-current h-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
d="M19 6h5V0h-5c-3.86 0-7 3.14-7 7v3H8v6h4v16h6V16h5l1-6h-6V7c0-.542.458-1 1-1z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const jsonResponse = await response.json();
|
||||
console.log(jsonResponse);
|
||||
<!--Main-->
|
||||
<div
|
||||
class="container pt-24 md:pt-36 mx-auto flex flex-wrap flex-col md:flex-row items-center"
|
||||
>
|
||||
<!--Left Col-->
|
||||
<div
|
||||
class="flex flex-col w-full xl:w-2/5 justify-center lg:items-start overflow-y-hidden"
|
||||
>
|
||||
<h1
|
||||
class="my-4 text-3xl md:text-5xl text-white opacity-75 font-bold leading-tight text-center md:text-left"
|
||||
>
|
||||
Main
|
||||
<span
|
||||
class="bg-clip-text text-transparent bg-gradient-to-r from-green-400 via-pink-500 to-purple-500"
|
||||
>
|
||||
Hero Message
|
||||
</span>
|
||||
to sell yourself!
|
||||
</h1>
|
||||
<p
|
||||
class="leading-normal text-base md:text-2xl mb-8 text-center md:text-left"
|
||||
>
|
||||
An AI-powered recipe generator that helps individuals
|
||||
with obesity take control of their diet.
|
||||
</p>
|
||||
|
||||
const classes = jsonResponse.predictions;
|
||||
<form
|
||||
class="bg-gray-800 opacity-75 w-full shadow-lg rounded-lg px-8 pt-6 pb-8 mb-4 h-64"
|
||||
>
|
||||
<button
|
||||
class="text-4xl w-full h-full bg-gradient-to-r from-purple-800 to-green-500 hover:from-pink-500 hover:to-green-500 text-white font-bold py-2 px-4 rounded-3xl focus:ring transform transition hover:scale-105 duration-300 ease-in-out flex items-center"
|
||||
onclick="document.getElementById('imageInput').click();"
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
id="imageInput"
|
||||
accept="image/*"
|
||||
required
|
||||
hidden
|
||||
/>
|
||||
<img
|
||||
src="upload.svg"
|
||||
class="h-1/2 pr-8 brightness-150"
|
||||
/>
|
||||
Upload your fridge
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
// Remove all underscores
|
||||
<!--Right Col-->
|
||||
<div class="w-full xl:w-3/5 p-12 overflow-hidden">
|
||||
<img
|
||||
class="mx-auto w-full md:w-4/5 transform -rotate-6 transition hover:scale-105 duration-700 ease-in-out hover:rotate-6"
|
||||
src="macbook.svg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
classes.forEach(str => {
|
||||
str.class = str.class.replace(/_/g, ' ');
|
||||
});
|
||||
<div class="mx-auto md:pt-16">
|
||||
<p
|
||||
class="text-blue-400 font-bold pb-8 lg:pb-6 text-center"
|
||||
></p>
|
||||
<div
|
||||
class="flex w-full justify-center md:justify-start pb-24 lg:pb-0 fade-in"
|
||||
>
|
||||
<img
|
||||
class="h-12 pr-12 transform hover:scale-125 duration-300 ease-in-out"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
class="h-12 transform hover:scale-125 duration-300 ease-in-out"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// Create input fields for classes with confidence >= 0.5
|
||||
|
||||
classes.filter((cls) => cls.confidence >= 0.5).forEach((cls) => {
|
||||
addInputField(cls.class);
|
||||
|
||||
});
|
||||
|
||||
// Create buttons for classes with confidence < 0.5
|
||||
|
||||
classes.filter((cls) => cls.confidence < 0.5).forEach((cls) => {
|
||||
addSuggestion(cls.class);
|
||||
});
|
||||
addSuggestion("salt and pepper");
|
||||
|
||||
|
||||
// Add event listener to "Add Class" button
|
||||
addClassBtn.onclick = () => {
|
||||
addInputField("");
|
||||
};
|
||||
|
||||
} catch ( exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function addInputField(content) {
|
||||
const inputField = document.createElement("input");
|
||||
inputField.type = "text";
|
||||
inputField.value = content;
|
||||
|
||||
const closeButton = document.createElement("button");
|
||||
closeButton.style.border = "none";
|
||||
closeButton.style.background = "transparent";
|
||||
closeButton.style.cursor = "pointer";
|
||||
closeButton.innerHTML = "x";
|
||||
|
||||
closeButton.addEventListener("click", () => {
|
||||
editableClassesDiv.removeChild(inputField);
|
||||
editableClassesDiv.removeChild(br);
|
||||
editableClassesDiv.removeChild(closeButton);
|
||||
});
|
||||
|
||||
editableClassesDiv.appendChild(inputField);
|
||||
editableClassesDiv.appendChild(closeButton);
|
||||
const br = document.createElement("br");
|
||||
editableClassesDiv.appendChild(br);
|
||||
}
|
||||
|
||||
function addSuggestion(content) {
|
||||
const button = document.createElement("button");
|
||||
button.textContent = content;
|
||||
button.onclick = () => {
|
||||
addInputField(content);
|
||||
button.remove();
|
||||
};
|
||||
suggestedClassesDiv.appendChild(button);
|
||||
}
|
||||
|
||||
async function createInference(query) {
|
||||
try {
|
||||
const response = await fetch("/completions", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ query })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const content = data.choices[0].message.content;
|
||||
return `<p>${content}</p>`;
|
||||
} catch (error) {
|
||||
return `<p>Error: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function generateIngredients() {
|
||||
// Get all input elements inside the div
|
||||
const inputs = editableClassesDiv.querySelectorAll("input");
|
||||
|
||||
// Initialize an empty string to hold the input values
|
||||
let values = "";
|
||||
|
||||
// Check if there are any input elements
|
||||
if (inputs.length === 0) {
|
||||
return; // Exit the function silently if there are no input elements
|
||||
}
|
||||
|
||||
// Loop through the input elements and add their values to the string
|
||||
inputs.forEach((input) => {
|
||||
if (values !== "") {
|
||||
values += ", ";
|
||||
}
|
||||
values += input.value;
|
||||
});
|
||||
|
||||
// Alert the input values
|
||||
const output = await createInference(values);
|
||||
|
||||
// Create a new div element to hold the output
|
||||
const outputDiv = document.createElement("div");
|
||||
outputDiv.textContent = output;
|
||||
|
||||
// Add the output div to the page
|
||||
document.body.appendChild(outputDiv);
|
||||
}
|
||||
</script>
|
||||
<!--Footer-->
|
||||
<div
|
||||
class="w-full pt-16 pb-6 text-sm text-center md:text-left fade-in"
|
||||
>
|
||||
<a
|
||||
class="text-gray-500 no-underline hover:no-underline"
|
||||
href="#"
|
||||
>© Fridge2Fit 2024</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
83
static/load.js
Normal file
83
static/load.js
Normal file
@ -0,0 +1,83 @@
|
||||
const uploadImage = async () => {
|
||||
const input = document.getElementById('imageInput');
|
||||
const file = input.files[0];
|
||||
const formData = new FormData();
|
||||
formData.append('image', file);
|
||||
|
||||
try {
|
||||
const response = await fetch('../upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to upload image');
|
||||
}
|
||||
|
||||
const jsonResponse = await response.json();
|
||||
console.log(jsonResponse);
|
||||
|
||||
const classes = jsonResponse.predictions;
|
||||
// classes[12] = { "class": "fishy fish", "confidence": 0.1 }
|
||||
|
||||
const editableClassesDiv = document.getElementById('editable-classes');
|
||||
const suggestedClassesDiv =
|
||||
document.getElementById('suggested-classes');
|
||||
const addClassBtn = document.getElementById('add-class-btn');
|
||||
|
||||
// Create input fields for classes with confidence >= 0.5
|
||||
|
||||
classes
|
||||
.filter((cls) => cls.confidence >= 0.5)
|
||||
.forEach((cls) => {
|
||||
const inputField = document.createElement('input');
|
||||
inputField.type = 'text';
|
||||
inputField.value = cls.class;
|
||||
|
||||
const closeButton = document.createElement('button');
|
||||
closeButton.style.border = 'none';
|
||||
closeButton.style.background = 'transparent';
|
||||
closeButton.style.cursor = 'pointer';
|
||||
closeButton.innerHTML = 'x';
|
||||
|
||||
closeButton.addEventListener('click', () => {
|
||||
editableClassesDiv.removeChild(inputField);
|
||||
editableClassesDiv.removeChild(br);
|
||||
editableClassesDiv.removeChild(closeButton);
|
||||
});
|
||||
|
||||
editableClassesDiv.appendChild(closeButton);
|
||||
editableClassesDiv.appendChild(inputField);
|
||||
const br = document.createElement('br');
|
||||
editableClassesDiv.appendChild(br);
|
||||
});
|
||||
|
||||
// Create buttons for classes with confidence < 0.5
|
||||
|
||||
classes
|
||||
.filter((cls) => cls.confidence < 0.5)
|
||||
.forEach((cls) => {
|
||||
const button = document.createElement('button');
|
||||
button.textContent = cls.class;
|
||||
button.onclick = () => {
|
||||
const inputField = document.createElement('input');
|
||||
inputField.type = 'text';
|
||||
inputField.value = cls.class;
|
||||
editableClassesDiv.appendChild(inputField);
|
||||
button.remove();
|
||||
};
|
||||
|
||||
suggestedClassesDiv.appendChild(button);
|
||||
});
|
||||
|
||||
// Add event listener to "Add Class" button
|
||||
|
||||
addClassBtn.onclick = () => {
|
||||
const inputField = document.createElement('input');
|
||||
inputField.type = 'text';
|
||||
editableClassesDiv.appendChild(inputField);
|
||||
};
|
||||
} catch (exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
};
|
3
static/upload.svg
Normal file
3
static/upload.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 1024 1024" class="icon" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M736.68 435.86a173.773 173.773 0 0 1 172.042 172.038c0.578 44.907-18.093 87.822-48.461 119.698-32.761 34.387-76.991 51.744-123.581 52.343-68.202 0.876-68.284 106.718 0 105.841 152.654-1.964 275.918-125.229 277.883-277.883 1.964-152.664-128.188-275.956-277.883-277.879-68.284-0.878-68.202 104.965 0 105.842zM285.262 779.307A173.773 173.773 0 0 1 113.22 607.266c-0.577-44.909 18.09-87.823 48.461-119.705 32.759-34.386 76.988-51.737 123.58-52.337 68.2-0.877 68.284-106.721 0-105.842C132.605 331.344 9.341 454.607 7.379 607.266 5.417 759.929 135.565 883.225 285.262 885.148c68.284 0.876 68.2-104.965 0-105.841z" fill="#4A5699" /><path d="M339.68 384.204a173.762 173.762 0 0 1 172.037-172.038c44.908-0.577 87.822 18.092 119.698 48.462 34.388 32.759 51.743 76.985 52.343 123.576 0.877 68.199 106.72 68.284 105.843 0-1.964-152.653-125.231-275.917-277.884-277.879-152.664-1.962-275.954 128.182-277.878 277.879-0.88 68.284 104.964 68.199 105.841 0z" fill="#C45FA0" /><path d="M545.039 473.078c16.542 16.542 16.542 43.356 0 59.896l-122.89 122.895c-16.542 16.538-43.357 16.538-59.896 0-16.542-16.546-16.542-43.362 0-59.899l122.892-122.892c16.537-16.542 43.355-16.542 59.894 0z" fill="#F39A2B" /><path d="M485.17 473.078c16.537-16.539 43.354-16.539 59.892 0l122.896 122.896c16.538 16.533 16.538 43.354 0 59.896-16.541 16.538-43.361 16.538-59.898 0L485.17 532.979c-16.547-16.543-16.547-43.359 0-59.901z" fill="#F39A2B" /><path d="M514.045 634.097c23.972 0 43.402 19.433 43.402 43.399v178.086c0 23.968-19.432 43.398-43.402 43.398-23.964 0-43.396-19.432-43.396-43.398V677.496c0.001-23.968 19.433-43.399 43.396-43.399z" fill="#E5594F" /></svg>
|
After Width: | Height: | Size: 1.8 KiB |
11
styles/style.css
Normal file
11
styles/style.css
Normal file
@ -0,0 +1,11 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
|
||||
|
||||
html {
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
}
|
8
tailwind.config.js
Normal file
8
tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['static/*.{html,js}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
Loading…
Reference in New Issue
Block a user