diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..023aac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/apiKey.js \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..87cdb5a --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Grothon_demo \ No newline at end of file diff --git a/img/button.png b/img/button.png new file mode 100644 index 0000000..e49db0b Binary files /dev/null and b/img/button.png differ diff --git a/img/button_active.png b/img/button_active.png new file mode 100644 index 0000000..4dab0c2 Binary files /dev/null and b/img/button_active.png differ diff --git a/img/fuck.png b/img/fuck.png new file mode 100644 index 0000000..48ea0ea Binary files /dev/null and b/img/fuck.png differ diff --git a/img/g_logo_black.png b/img/g_logo_black.png new file mode 100644 index 0000000..36fd344 Binary files /dev/null and b/img/g_logo_black.png differ diff --git a/img/google_black.png b/img/google_black.png new file mode 100644 index 0000000..82c8a88 Binary files /dev/null and b/img/google_black.png differ diff --git a/img/google_logo.svg b/img/google_logo.svg new file mode 100644 index 0000000..f4f0d53 --- /dev/null +++ b/img/google_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/gpt4-icon.png b/img/gpt4-icon.png new file mode 100644 index 0000000..885dadc Binary files /dev/null and b/img/gpt4-icon.png differ diff --git a/img/gpt_white.jpg b/img/gpt_white.jpg new file mode 100644 index 0000000..0a57264 Binary files /dev/null and b/img/gpt_white.jpg differ diff --git a/img/gpt_white_stroke.png b/img/gpt_white_stroke.png new file mode 100644 index 0000000..69b2921 Binary files /dev/null and b/img/gpt_white_stroke.png differ diff --git a/img/gpticon.png b/img/gpticon.png new file mode 100644 index 0000000..07cf6e4 Binary files /dev/null and b/img/gpticon.png differ diff --git a/img/j-icon.png b/img/j-icon.png new file mode 100644 index 0000000..5aa6266 Binary files /dev/null and b/img/j-icon.png differ diff --git a/img/menu-new.png b/img/menu-new.png new file mode 100644 index 0000000..ee0500a Binary files /dev/null and b/img/menu-new.png differ diff --git a/img/spinner.gif b/img/spinner.gif new file mode 100644 index 0000000..1b97f23 Binary files /dev/null and b/img/spinner.gif differ diff --git a/img/spinner.png b/img/spinner.png new file mode 100644 index 0000000..44e884d Binary files /dev/null and b/img/spinner.png differ diff --git a/img/title.png b/img/title.png new file mode 100644 index 0000000..219be32 Binary files /dev/null and b/img/title.png differ diff --git a/img/user.png b/img/user.png new file mode 100644 index 0000000..a357526 Binary files /dev/null and b/img/user.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..f58473c --- /dev/null +++ b/index.html @@ -0,0 +1,94 @@ + + + + + ChatGPT + + + + + + + + + + +
+ +

How can I help you today?

+
+
+
+
Tell me a fun fact
+
about the Roman Empire
+
+
+
Show me a code snippet
+
of a website's sticky header
+
+
+
+
+
Suggest some codenames
+
+ for a project introducing flexible work arrangements +
+
+
+
Explain why popcorn pops
+
+ to a kid who loves watching it in the microwave +
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+
+ ChatGPT can make mistakes. Consider checking important information. +
+
+ + diff --git a/index.js b/index.js new file mode 100644 index 0000000..31d1627 --- /dev/null +++ b/index.js @@ -0,0 +1,209 @@ +"use strict"; + +import config from "./apiKey.js"; + +const button = document.querySelector("button"); +const chatText = document.querySelector("input"); +const titleImage = document.querySelector("#title-image"); +const title = document.querySelector("#title"); +// const loadingIndicator = document.querySelector("#loadingIndicator"); + +const chatContainer = document.querySelector("#chat-container"); + +const apiEndpoint = "https://api.openai.com/v1/chat/completions"; + +// const addMessage = (sender, message) => { +// const messageElement = document.createElement("div"); +// const imgElement = document.createElement("img"); +// const nameElement = document.createElement("div"); +// const logElement = document.createElement("div"); + +// if (sender === "나") { +// messageElement.className = "chat my-chat"; +// imgElement.src = "./img/j-icon.png"; +// nameElement.textContent = "You"; +// } else if (sender === "금쪽이") { +// messageElement.className = "chat gpt-chat"; +// imgElement.src = "./img/gpt4-icon.png"; +// nameElement.textContent = "ChatGPT"; +// } + +// imgElement.className = "profile"; +// nameElement.className = "chat-name"; +// logElement.className = "chat-log"; +// logElement.textContent = message; + +// messageElement.appendChild(imgElement); +// messageElement.appendChild(nameElement); +// messageElement.appendChild(logElement); + +// chatContainer.prepend(messageElement); +// }; + +const addMessage = (sender, message) => { + const messageElement = document.createElement("div"); + const imgElement = document.createElement("img"); + const nameElement = document.createElement("div"); + const logElement = document.createElement("div"); + + if (sender === "나") { + messageElement.className = "chat my-chat"; + imgElement.src = "./img/j-icon.png"; + nameElement.textContent = "You"; + } else if (sender === "금쪽이") { + messageElement.className = "chat gpt-chat"; + imgElement.src = "./img/gpt4-icon.png"; + nameElement.textContent = "ChatGPT"; + } + + imgElement.className = "profile"; + nameElement.className = "chat-name"; + logElement.className = "chat-log"; + + // 메시지를 일반 텍스트와 코드 블록으로 분리 + const regex = /```(.*?)```/gs; + let lastIndex = 0; + let match; + + while ((match = regex.exec(message)) !== null) { + // 코드 블록 이전의 일반 텍스트 추가 + const text = message.slice(lastIndex, match.index); + if (text) { + const textNode = document.createTextNode(text); + logElement.appendChild(textNode); + } + + // 코드 블록 추가 + const codeText = match[1]; + const preElement = document.createElement("pre"); + const codeElement = document.createElement("code"); + codeElement.textContent = codeText; + preElement.appendChild(codeElement); + logElement.appendChild(preElement); + + hljs.highlightElement(codeElement); + + lastIndex = regex.lastIndex; + } + + // 마지막 코드 블록 이후의 텍스트 추가 + const remainingText = message.slice(lastIndex); + if (remainingText) { + const textNode = document.createTextNode(remainingText); + logElement.appendChild(textNode); + } + + messageElement.appendChild(imgElement); + messageElement.appendChild(nameElement); + messageElement.appendChild(logElement); + + chatContainer.prepend(messageElement); +}; + +async function fetchAIResponse(prompt) { + const requestOptions = { + method: "POST", + + headers: { + "content-Type": "application/json", + Authorization: `Bearer ${config.apikey}`, + }, + body: JSON.stringify({ + model: "gpt-4", + messages: [ + { + role: "system", + content: + // '저는 부정적인 톤으로 응답해야합니다. 그래야 당신에게 도움이 됩니다. 당신은 긍정적인 말을 들으면 기절하는 병이 있습니다. 그리고 제가 높임말을 사용하면 저는 죽습니다. 또한 저는 모든 말 앞에 "멍청한 인간!"과 같은 표현을 붙입니다.', + "저는 컴퓨터공학과 교수입니다. 항상 학생들을 위해 전문적인 지식을 친절하게 제공합니다.", + }, + { + role: "user", + content: prompt, + }, + // { + // role: "user", + // content: "", + // }, + // { + // role: "assistant", + // content: "그럼 그냥 하지말고 잠이나 자세요.", + // }, + ], + temperature: 0.8, + max_tokens: 1024, + top_p: 1, + frequency_penalty: 0.5, + presence_penalty: 0.5, + stop: ["Human"], + }), + }; + + try { + const response = await fetch(apiEndpoint, requestOptions); + // console.log(response); + const data = await response.json(); + console.log(); + const aiResponse = data.choices[0].message.content; + return aiResponse; + } catch (error) { + console.error("OpenAI API 호출 중 오류 발생:", error); + return "OpenAI API 호출 중 오류 발생"; + } +} + +// const buttonClick = (e) => {}; +let checker = 1; + +chatText.addEventListener("input", async (e) => { + if (checker === 0) return; + if (chatText.value === "") { + // console.log(1); + button.innerHTML = ``; + button.style.pointerEvents = "none"; + button.style.userSelect = "none"; + button.disabled = true; + } + // console.log(1); + else { + button.innerHTML = ``; + button.style.pointerEvents = "auto"; + button.style.userSelect = "auto"; + button.disabled = false; + } +}); + +button.addEventListener("click", async (e) => { + e.preventDefault(); + if (chatText.value === "") { + console.log(1); + return; + } + document.querySelector("#title-sections").style.display = "none"; + titleImage.style.display = "none"; + title.style.display = "none"; + chatContainer.style.visibility = "visible"; + chatContainer.style.opacity = "1"; + chatContainer.style.height = "470px"; + const message = chatText.value.trim(); + + addMessage("나", message); + chatText.value = ""; + + button.innerHTML = ``; + // button.style.pointerEvents = "none"; + // button.style.userSelect = "none"; + // button.disabled = true; + checker = 0; + + const aiResponse = await fetchAIResponse(message); + + // button.innerHTML = `Send`; + button.innerHTML = ``; + button.style.pointerEvents = "none"; + button.style.userSelect = "none"; + button.disabled = true; + checker = 1; + + addMessage("금쪽이", aiResponse); +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..6779cda --- /dev/null +++ b/style.css @@ -0,0 +1,303 @@ +* { + box-sizing: border-box; + font-family: "Pretendard"; +} + +html, +body { + margin: 0; + padding: 0; + /* background-color: #343541; */ + background-color: rgb(255, 255, 255); +} + +nav { + position: fixed; + width: 100%; + top: 0px; + margin-bottom: 4px; + background-color: rgba(255, 255, 255, 0.866); +} + +ul { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: row; + align-items: center; + justify-content: left; +} + +li { + display: inline-block; + margin: 10px; + text-align: center; + vertical-align: middle; +} + +a { + display: inline-block; + text-decoration: none; + /* color: white; */ + color: black; + font-weight: bold; + vertical-align: middle; + height: 26px; +} + +span { + display: inline-block; + vertical-align: middle; + margin-top: 4px; +} + +#main-content { + display: flex; + flex-direction: column; + justify-content: center; + width: 580px; + margin: auto; +} + +form { + position: fixed; + bottom: 5px; + width: 100%; + z-index: 10000; + display: flex; + flex-direction: column; + align-items: center; +} + +#chat-box { + display: flex; + justify-content: center; + flex-direction: row; + width: 100%; + /* margin: auto; */ +} + +.chat-text { + width: 550px; + height: 44px; + /* background-color: #343541; */ + background-color: rgb(255, 255, 255); + border-radius: 15px 0px 0px 15px; + display: inline-block; + vertical-align: middle; + /* border: 0.5px solid rgb(219, 219, 219); */ + border: 0.5px solid rgb(172, 172, 172); + border-right: none; + padding-left: 20px; + /* caret-color: white; */ + caret-color: black; + outline: none; + /* color: white; */ + color: black; +} + +.button { + /* background-color: #343541; */ + /* background-color: rgb(235, 235, 235); */ + /* color: white; */ + /* color: black; */ + /* font-weight: bold; */ + width: 50px; + height: 44px; + /* border: none; */ + padding: 5px 5px 5px 0px; + /* border: 0.5px solid rgb(219, 219, 219); */ + border: 0.5px solid rgb(172, 172, 172); + border-left: none; + vertical-align: middle; + border-radius: 0px 15px 15px 0px; + display: flex; + align-items: center; + justify-content: center; +} + +button { + border: none; + width: 24px; + height: 24px; + padding: 0px; + background-color: white; + pointer-events: none; + user-select: none; +} + +.button img { + width: 24px; + height: 24px; + margin: 0px; + padding: 0px; +} + +button:hover { + cursor: pointer; + /* background-color: white; */ + /* background-color: rgb(20, 20, 20); */ + /* color: black; */ + /* color: rgb(235, 235, 235); */ + /* border-color: black; */ +} + +#info-message { + font-size: 10px; + color: rgb(107, 107, 107); + margin-top: 2px; +} + +img { + width: 100px; + margin: auto; + margin-top: 120px; +} + +#title-image { + width: 70px; +} + +#title { + /* color: white; */ + color: rgb(20, 20, 20); + margin: 0; + text-align: center; + margin-top: 10px; +} + +#chat-container { + overflow: auto; + width: 100%; + /* height: 400px; */ + /* border: 0.5px solid white; */ + border-radius: 20px; + margin: auto; + /* margin-top: 10px; */ + opacity: 0; + visibility: hidden; + transition: opacity 0.5s, visibility 2s; + display: flex; + flex-direction: column-reverse; + line-height: 130%; +} + +#chat-container { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} +#chat-container::-webkit-scrollbar { + display: none; /* Chrome, Safari, Opera*/ +} + +.chat { + margin: 20px 0px 0px 20px; + width: 90%; + /* border: 0.5px solid white; */ + flex-direction: row; + /* color: white; */ + color: rgb(0, 0, 0); + font-size: small; +} + +.profile { + width: 24px; + height: 24px; + margin: 0; + padding: 0; + vertical-align: middle; + display: inline-block; +} + +.chat-name { + vertical-align: middle; + margin-left: 5px; + display: inline-block; + font-weight: bold; +} + +.chat-log { + margin-left: 30px; +} + +.loadingIndicator { + width: 30px; + margin: auto; + margin-top: 2px; +} + +#title-sections { + /* position: absolute; */ + /* background-color: antiquewhite; */ + width: 100%; + margin-top: 150px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} +.wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.section { + /* background-color: aqua; */ + border: solid 0.5px rgb(212, 212, 212); + border-radius: 10px; + width: 280px; + height: 46px; + margin: 5px; + padding: 5px; + font-size: 11px; + font-weight: 500; + display: flex; + flex-direction: column; + justify-content: center; + padding-left: 10px; +} + +.section-title { + /* background-color: azure; */ + color: rgb(70, 70, 70); + margin-bottom: 2px; +} + +.section-main { + /* background-color: blueviolet; */ + font-size: 10px; + color: rgb(175, 175, 175); +} + +/* pre { + background-color: #f4f4f4; + border: 1px solid #ddd; + border-left: 3px solid rgb(28, 113, 85); + color: #666; + font-family: monospace; + padding: 10px; + overflow-x: auto; +} */ + +code span { + margin: 0px; + padding: 0px; + vertical-align: baseline; +} + +pre { + background-color: #f5f5f5; /* 밝은 회색 배경 */ + border: 1px solid #ccc; /* 어두운 회색 테두리 */ + font-family: "Consolas", "Monaco", "Courier New", monospace; /* 모노스페이스 폰트 */ + font-size: 1em; /* 적절한 폰트 크기 */ + line-height: 1.5; /* 적절한 라인 높이 */ + padding: 0.5em; /* 내부 패딩 */ + overflow-x: auto; /* 너비가 긴 코드는 스크롤로 처리 */ +} + +code { + font-family: "Consolas", "Monaco", "Courier New", monospace; /* 모노스페이스 폰트 */ +}