-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0bc5677
Showing
6 changed files
with
496 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"# Gemini_Clone" |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<!DOCTYPE html> | ||
<html lang="en" dir="ltr"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<!-- <meta http-equiv="refresh" content="3"> --> | ||
<title>Gemini Clone</title> | ||
<!-- Linking Google Fonts for Icons --> | ||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" /> | ||
<link rel="stylesheet" href="style.css"> | ||
</head> | ||
<body> | ||
<header class="header"> | ||
<!-- Header Greetings --> | ||
<h2 class="title">Hello, there</h2> | ||
<h4 class="subtitle">How can I help you today?</h4> | ||
|
||
<!-- Suggestion List --> | ||
<ul class="suggestion-list"> | ||
<li class="suggestion"> | ||
<h4 class="text">Help me plan a game night with my 5 best friends for under $100.</h4> | ||
<span class="icon material-symbols-outlined">draw</span> | ||
</li> | ||
<li class="suggestion"> | ||
<h4 class="text">What are the best tips to improve my public speaking skills?</h4> | ||
<span class="icon material-symbols-outlined">lightbulb</span> | ||
</li> | ||
<li class="suggestion"> | ||
<h4 class="text">Can you help me find the lastest news on web development?</h4> | ||
<span class="icon material-symbols-outlined">explore</span> | ||
</li> | ||
<li class="suggestion"> | ||
<h4 class="text">Write javascript code to sum all element in an arrey.</h4> | ||
<span class="icon material-symbols-outlined">code</span> | ||
</li> | ||
</ul> | ||
</header> | ||
|
||
<!-- Chat List / Container --> | ||
<div class="chat-list"></div> | ||
|
||
<!-- Typing Area --> | ||
<div class="typing-area"> | ||
<form action="#" class="typing-form"> | ||
<div class="input-wrapper"> | ||
<input type="text" placeholder="Enter a promet here" class="typing-input" required> | ||
<button class="icon material-symbols-outlined">send</button> | ||
</div> | ||
<div class="action-buttons"> | ||
<span id="toggle-theme-button" class="icon material-symbols-outlined">light_mode</span> | ||
<span id="delete-chat-button" class="icon material-symbols-outlined">delete</span> | ||
</div> | ||
</form> | ||
<p class="disclaimer-text">Gemini may display inaccurate info, including about people, so double-check its responses.</p> | ||
</div> | ||
|
||
<script src="script.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
const typingForm = document.querySelector(".typing-form"); | ||
const chatList = document.querySelector(".chat-list"); | ||
const suggestions = document.querySelectorAll(".suggestion-list .suggestion"); | ||
const toggleThemeButton = document.querySelector("#toggle-theme-button"); | ||
const deleteChatButton = document.querySelector("#delete-chat-button"); | ||
|
||
let userMessage = null; | ||
let isResponseGenerating = false; | ||
// Api Gonfiguration | ||
const API_KEY = "AIzaSyD4LpYrsW8mdHYD1jbHm8RT8VzdIZ83u6E"; | ||
const API_URL = `https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent?key=${API_KEY}`; | ||
|
||
const loadLocalstorageData = () => { | ||
const savedChats = localStorage.getItem("savedChats"); | ||
const isLightMode = (localStorage.getItem("themeColor") === "light_mode"); | ||
|
||
// Apply the stored theme | ||
document.body.classList.toggle("light_mode", isLightMode); | ||
toggleThemeButton.innerText = isLightMode ? "dark_mode" : "light_mode"; | ||
|
||
// Restore saved chats | ||
chatList.innerHTML = savedChats || ""; | ||
|
||
document.body.classList.toggle("hide-header", savedChats); | ||
chatList.scrollTo(0, chatList.scrollHeight); // Scroll to the bottom | ||
} | ||
loadLocalstorageData(); | ||
|
||
// Create a new message element and return it | ||
const createMessageElement = (content, ...classes) =>{ | ||
const div = document.createElement("div"); | ||
div.classList.add("message", ...classes); | ||
div.innerHTML = content; | ||
return div; | ||
} | ||
|
||
// showTyping effect by displaying words one by one | ||
const showTypingEffect = (text, textElement, incomingMessageDiv) => { | ||
const words = text.split(' '); | ||
let currentWorldIndex = 0; | ||
|
||
const typingIntervel = setInterval(() => { | ||
// Append each word to the text element with a space | ||
textElement.innerText += (currentWorldIndex === 0 ? '' : ' ') + words[currentWorldIndex++]; | ||
incomingMessageDiv.querySelector(".icon").classList.add("hide"); | ||
// If all words are displayed | ||
if (currentWorldIndex === words.length) { | ||
clearInterval(typingIntervel); | ||
isResponseGenerating = false; | ||
incomingMessageDiv.querySelector(".icon").classList.add("hide"); | ||
localStorage.setItem("savedChats", chatList.innerHTML);// Save chats to local storage | ||
} | ||
chatList.scrollTo(0, chatList.scrollHeight); // Scroll to the bottom | ||
}, 75); | ||
} | ||
|
||
// Fetch response from the API based on user message | ||
const generateAPIResponse = async (incomingMessageDiv) => { | ||
const textElement = incomingMessageDiv.querySelector(".text");// Get text element. | ||
// Send a POST request to the API eith the user's message | ||
try { | ||
const response = await fetch(API_URL, { | ||
method: "POST", | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify({ | ||
contents: [{ | ||
role: "user", | ||
parts: [{ text: userMessage }] | ||
}] | ||
}) | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) throw new Error(data.error.message); | ||
|
||
// Get the API response text and remove asterisks from it. | ||
const apiResponse = data?.candidates[0].content.parts[0].text.replace(/\*\*(.*?)\*\*/g, '$1'); | ||
showTypingEffect(apiResponse, textElement, incomingMessageDiv); | ||
} catch (error) { | ||
isResponseGenerating = false; | ||
textElement.innerText = error.message; | ||
textElement.classList.add("error"); | ||
} finally { | ||
incomingMessageDiv.classList.remove("loading"); | ||
} | ||
} | ||
|
||
// Show a loading animation while waiting for the API response | ||
const showLoadingAnimation = () => { | ||
const html = `<div class="message-content"> | ||
<img src="img/gemini.svg" alt="Gemini Image" class="avater"> | ||
<p class="text"></p> | ||
<div class="loading-indicator"> | ||
<div class="loading-bar"></div> | ||
<div class="loading-bar center"></div> | ||
<div class="loading-bar"></div> | ||
</div> | ||
</div> | ||
<span onclick="copyMessage(this)" class="icon material-symbols-outlined">content_copy</span>`; | ||
const incomingMessageDiv = createMessageElement(html, "incoming", "loading"); | ||
chatList.appendChild(incomingMessageDiv); | ||
|
||
chatList.scrollTo(0, chatList.scrollHeight); // Scroll to the bottom | ||
generateAPIResponse(incomingMessageDiv); | ||
} | ||
// Copy message text to the clipboard | ||
const copyMessage = (copyIcon) => { | ||
const messageText = copyIcon.parentElement.querySelector(".text").innerText; | ||
navigator.clipboard.writeText(messageText); | ||
copyIcon.innerHTML = "done"; // Show tick icon | ||
setTimeout(() => copyIcon.innerText = "content_copy", 1000); // Revert icon after 1 second. | ||
} | ||
// Handle sending outgoing chat messages | ||
const handleOutgoingChat = () => { | ||
userMessage = typingForm.querySelector(".typing-input").value.trim() || userMessage; | ||
if (!userMessage || isResponseGenerating) return; //Exit if there is no message | ||
isResponseGenerating = true; | ||
const html = `<div class="message-content"> | ||
<img src="img/user.jpg" alt="User Image" class="avater"> | ||
<p class="text"></p> | ||
</div>`; | ||
const outgoingMessageDiv = createMessageElement(html, "outgoing"); | ||
outgoingMessageDiv.querySelector(".text").innerText = userMessage; | ||
chatList.appendChild(outgoingMessageDiv); | ||
typingForm.reset(); // Clear input field | ||
chatList.scrollTo(0, chatList.scrollHeight); // Scroll to the bottom | ||
document.body.classList.add("hide-header"); // Hide the hesder once chat start | ||
setTimeout(showLoadingAnimation, 500); // Show loading animation after a delay | ||
} | ||
// set userMessage and handly outgoing chat when a suggestion is clicked | ||
suggestions.forEach(suggestion => { | ||
suggestion.addEventListener("click", () => { | ||
userMessage = suggestion.querySelector(".text").innerText; | ||
handleOutgoingChat(); | ||
}); | ||
}); | ||
// Toggle between light and dark themes | ||
toggleThemeButton.addEventListener("click", () => { | ||
const isLightMode = document.body.classList.toggle("light_mode"); | ||
localStorage.setItem("themeColor", isLightMode ? "light_mode" : "dark_mode"); | ||
toggleThemeButton.innerText = isLightMode ? "dark_mode" : "light_mode"; | ||
}); | ||
// Delete all chats from local storage when button is clicked | ||
deleteChatButton.addEventListener("click", () => { | ||
if (confirm("Are you sure you want to delete all message?")) { | ||
localStorage.removeItem("savedChats"); | ||
loadLocalstorageData(); | ||
} | ||
}); | ||
// Prevent defult form submission and handle outdoinig chat | ||
typingForm.addEventListener("submit", (e) => { | ||
e.preventDefault(); | ||
handleOutgoingChat(); | ||
}); |
Oops, something went wrong.