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
32+ }
33+
34+ const getChatResponse = async ( incomingChatDiv ) => {
35+ const API_URL = "https://api.openai.com/v1/completions" ;
36+ const pElement = document . createElement ( "p" ) ;
37+
38+ // Define the properties and data for the API request
39+ const requestOptions = {
40+ method : "POST" ,
41+ headers : {
42+ "Content-Type" : "application/json" ,
43+ "Authorization" : `Bearer ${ API_KEY } `
44+ } ,
45+ 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
52+ } )
53+ }
54+
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 ) ;
97+ }
98+
99+ const handleOutgoingChat = ( ) => {
100+ userText = chatInput . value . trim ( ) ;
101+ if ( ! userText ) return ;
102+
103+ 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 ) ;
118+ }
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+
135+ chatInput . addEventListener ( "input" , ( ) => {
136+ chatInput . style . height = `${ initialInputHeight } px` ;
137+ chatInput . style . height = `${ chatInput . scrollHeight } px` ;
138+ } ) ;
139+
140+ chatInput . addEventListener ( "keydown" , ( e ) => {
141+
142+ if ( e . key === "Enter" && ! e . shiftKey && window . innerWidth > 800 ) {
143+ e . preventDefault ( ) ;
144+ handleOutgoingChat ( ) ;
145+ }
146+ } ) ;
147+
148+ loadDataFromLocalstorage ( ) ;
149+ sendButton . addEventListener ( "click" , handleOutgoingChat ) ;
0 commit comments