1- const chatInput = document . querySelector ( "#chat-input" ) ;
2- const sendButton = document . querySelector ( "#send-btn" ) ;
3- const chatContainer = document . querySelector ( ".chat-container" ) ;
4- const themeButton = document . querySelector ( "#theme-btn" ) ;
5- const deleteButton = document . querySelector ( "#delete-btn" ) ;
6-
7- let userText = null ;
8- const API_KEY = "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" ; // Paste your API key here
9-
10- const loadDataFromLocalstorage = ( ) => {
11- // Load saved chats and theme from local storage and apply/add on the page
12- const themeColor = localStorage . getItem ( "themeColor" ) ;
13-
14- document . body . classList . toggle ( "light-mode" , themeColor === "light_mode" ) ;
15- themeButton . innerText = document . body . classList . contains ( "light-mode" ) ? "dark_mode" : "light_mode" ;
16-
17- const defaultText = `<div class="default-text">
18- <h1>ChatGPT Clone</h1>
19- <p>Start a conversation and explore the power of AI.<br> Your chat history will be displayed here.</p>
20- </div>`
21-
22- chatContainer . innerHTML = localStorage . getItem ( "all-chats" ) || defaultText ;
23- chatContainer . scrollTo ( 0 , chatContainer . scrollHeight ) ; // Scroll to bottom of the chat container
24- }
25-
26- const createChatElement = ( content , className ) => {
27- // Create new div and apply chat, specified class and set html content of div
28- const chatDiv = document . createElement ( "div" ) ;
29- chatDiv . classList . add ( "chat" , className ) ;
30- chatDiv . innerHTML = content ;
31- return chatDiv ; // Return the created chat div
1+ const chatInput = document . querySelector ( ".chat-input textarea" ) ;
2+ const sendChatBtn = document . querySelector ( ".chat-input span" ) ;
3+ const chatbox = document . querySelector ( ".chatbox" ) ;
4+ const chatbotToggler = document . querySelector ( ".chatbot-toggler" ) ;
5+ const closeBtn = document . querySelector ( ".close-btn" ) ;
6+
7+ let userMessage = null ; //variable to store user's message
8+
9+ const API_KEY = "PASTE YOUR API KEY" ; //Paste your API key here....How to generate is explained in README.md
10+ const inputInitHeight = chatInput . scrollHeight ;
11+
12+ const createChatLi = ( message , className ) => {
13+ //create a chat <li> element with passed message and className
14+ const chatLi = document . createElement ( "li" ) ;
15+ chatLi . classList . add ( "chat" , `${ className } ` ) ;
16+ let chatContent = className === "outgoing" ? `<p></p>` : `<span class="material-symbols-outlined">smart_toy</span><p></p>` ;
17+ chatLi . innerHTML = chatContent ;
18+ chatLi . querySelector ( "p" ) . textContent = message ;
19+ return chatLi ; //return the chat <li> element
3220}
3321
34- const getChatResponse = async ( incomingChatDiv ) => {
35- const API_URL = "https://api.openai.com/v1/completions" ;
36- const pElement = document . createElement ( "p" ) ;
22+ const generateResponse = ( chatElement ) => {
23+ const API_URL = "https://api.openai.com/v1/chat/ completions" ;
24+ const messageElement = chatElement . querySelector ( "p" ) ;
3725
38- // Define the properties and data for the API request
26+ //define the properties and message for the api request
3927 const requestOptions = {
4028 method : "POST" ,
4129 headers : {
4230 "Content-Type" : "application/json" ,
4331 "Authorization" : `Bearer ${ API_KEY } `
4432 } ,
4533 body : JSON . stringify ( {
46- model : "text-davinci-003" ,
47- prompt : userText ,
48- max_tokens : 2048 ,
49- temperature : 0.2 ,
50- n : 1 ,
51- stop : null
34+ model : "gpt-3.5-turbo" ,
35+ messages : [ { role : "user" , content : userMessage } ] ,
5236 } )
5337 }
5438
55- // Send POST request to API, get response and set the reponse as paragraph element text
56- try {
57- const response = await ( await fetch ( API_URL , requestOptions ) ) . json ( ) ;
58- pElement . textContent = response . choices [ 0 ] . text . trim ( ) ;
59- } catch ( error ) { // Add error class to the paragraph element and set error text
60- pElement . classList . add ( "error" ) ;
61- pElement . textContent = "Oops! Something went wrong while retrieving the response. Please try again." ;
62- }
63-
64- // Remove the typing animation, append the paragraph element and save the chats to local storage
65- incomingChatDiv . querySelector ( ".typing-animation" ) . remove ( ) ;
66- incomingChatDiv . querySelector ( ".chat-details" ) . appendChild ( pElement ) ;
67- localStorage . setItem ( "all-chats" , chatContainer . innerHTML ) ;
68- chatContainer . scrollTo ( 0 , chatContainer . scrollHeight ) ;
69- }
70-
71- const copyResponse = ( copyBtn ) => {
72- // Copy the text content of the response to the clipboard
73- const reponseTextElement = copyBtn . parentElement . querySelector ( "p" ) ;
74- navigator . clipboard . writeText ( reponseTextElement . textContent ) ;
75- copyBtn . textContent = "done" ;
76- setTimeout ( ( ) => copyBtn . textContent = "content_copy" , 1000 ) ;
77- }
78-
79- const showTypingAnimation = ( ) => {
80- // Display the typing animation and call the getChatResponse function
81- const html = `<div class="chat-content">
82- <div class="chat-details">
83- <img src="images/chatbot.png" alt="chatbot-img">
84- <div class="typing-animation">
85- <div class="typing-dot" style="--delay: 0.2s"></div>
86- <div class="typing-dot" style="--delay: 0.3s"></div>
87- <div class="typing-dot" style="--delay: 0.4s"></div>
88- </div>
89- </div>
90- <span onclick="copyResponse(this)" class="material-symbols-rounded">content_copy</span>
91- </div>` ;
92- // Create an incoming chat div with typing animation and append it to chat container
93- const incomingChatDiv = createChatElement ( html , "incoming" ) ;
94- chatContainer . appendChild ( incomingChatDiv ) ;
95- chatContainer . scrollTo ( 0 , chatContainer . scrollHeight ) ;
96- getChatResponse ( incomingChatDiv ) ;
39+ //send POST request to API, get response
40+ fetch ( API_URL , requestOptions ) . then ( res => res . json ( ) ) . then ( data => {
41+ messageElement . textContent = data . choices [ 0 ] . message . content . trim ( ) ;
42+ } ) . catch ( ( ) => {
43+ messageElement . classList . add ( "error" ) ;
44+ messageElement . textContent = "Oops! Something went wrong. Please try again." ;
45+ } ) . finally ( ( ) => chatbox . scrollTo ( 0 , chatbox . scrollHeight ) ) ;
9746}
9847
99- const handleOutgoingChat = ( ) => {
100- userText = chatInput . value . trim ( ) ;
101- if ( ! userText ) return ;
48+ const handleChat = ( ) => {
49+ userMessage = chatInput . value . trim ( ) ; //Get user enetred message and remove extra whitespace
50+ if ( ! userMessage ) return ;
10251
52+ //clear the input textarea and set its height to default
10353 chatInput . value = "" ;
104- chatInput . style . height = `${ initialInputHeight } px` ;
105-
106- const html = `<div class="chat-content">
107- <div class="chat-details">
108- <img src="images/user.png" alt="user-img">
109- <p>${ userText } </p>
110- </div>
111- </div>` ;
112-
113- const outgoingChatDiv = createChatElement ( html , "outgoing" ) ;
114- chatContainer . querySelector ( ".default-text" ) ?. remove ( ) ;
115- chatContainer . appendChild ( outgoingChatDiv ) ;
116- chatContainer . scrollTo ( 0 , chatContainer . scrollHeight ) ;
117- setTimeout ( showTypingAnimation , 500 ) ;
54+ chatInput . style . height = `${ inputInitHeight } px` ; //resetting the textarea height to its default height once a message is sent
55+
56+ //append the user's message to the chatbox
57+ chatbox . appendChild ( createChatLi ( userMessage , "outgoing" ) ) ;
58+ chatbox . scrollTo ( 0 , chatbox . scrollHeight ) ;
59+
60+ setTimeout ( ( ) => {
61+ //Display "Thinking..." message while waiting for the response
62+ const incomingChatLi = createChatLi ( "Thinking..." , "incoming" ) ;
63+ chatbox . appendChild ( incomingChatLi ) ;
64+ chatbox . scrollTo ( 0 , chatbox . scrollHeight ) ;
65+ generateResponse ( incomingChatLi ) ;
66+ } , 600 ) ;
11867}
119-
120- deleteButton . addEventListener ( "click" , ( ) => {
121- if ( confirm ( "Are you sure you want to delete all the chats?" ) ) {
122- localStorage . removeItem ( "all-chats" ) ;
123- loadDataFromLocalstorage ( ) ;
124- }
125- } ) ;
126-
127- themeButton . addEventListener ( "click" , ( ) => {
128- document . body . classList . toggle ( "light-mode" ) ;
129- localStorage . setItem ( "themeColor" , themeButton . innerText ) ;
130- themeButton . innerText = document . body . classList . contains ( "light-mode" ) ? "dark_mode" : "light_mode" ;
131- } ) ;
132-
133- const initialInputHeight = chatInput . scrollHeight ;
134-
13568chatInput . addEventListener ( "input" , ( ) => {
136- chatInput . style . height = `${ initialInputHeight } px` ;
69+ //Adjust the height of the input textarea based on its content
70+ chatInput . style . height = `${ inputInitHeight } px` ;
13771 chatInput . style . height = `${ chatInput . scrollHeight } px` ;
13872} ) ;
13973
14074chatInput . addEventListener ( "keydown" , ( e ) => {
141-
142- if ( e . key === "Enter" && ! e . shiftKey && window . innerWidth > 800 ) {
75+ //if enter key is pressed without shift key and the window
76+ //width is greater than the 800px, handle the chat
77+ if ( e . key === "Enter" && ! e . shiftKey && window . innerWidth > 800 ) {
14378 e . preventDefault ( ) ;
144- handleOutgoingChat ( ) ;
79+ handleChat ( ) ;
14580 }
14681} ) ;
14782
148- loadDataFromLocalstorage ( ) ;
149- sendButton . addEventListener ( "click" , handleOutgoingChat ) ;
83+ sendChatBtn . addEventListener ( "click" , handleChat ) ;
84+ closeBtn . addEventListener ( "click" , ( ) => document . body . classList . remove ( "show-chatbot" ) ) ;
85+ chatbotToggler . addEventListener ( "click" , ( ) => document . body . classList . toggle ( "show-chatbot" ) ) ;
86+
0 commit comments