From 82ce98431e0a8d1d2c673c97e82c8d9b24e5b038 Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Sun, 12 Feb 2023 11:34:58 -0300 Subject: [PATCH 01/22] style: create application style --- assets/css/global.css | 32 ++++++ assets/css/index.css | 188 ++++++++++++++++++++++++++++++++++++ assets/img/send.png | Bin 0 -> 1033 bytes {src => assets}/js/index.js | 0 index.html | 21 ++-- src/css/index.css | 0 6 files changed, 233 insertions(+), 8 deletions(-) create mode 100644 assets/css/global.css create mode 100644 assets/css/index.css create mode 100644 assets/img/send.png rename {src => assets}/js/index.js (100%) delete mode 100644 src/css/index.css diff --git a/assets/css/global.css b/assets/css/global.css new file mode 100644 index 0000000..8be1361 --- /dev/null +++ b/assets/css/global.css @@ -0,0 +1,32 @@ +:root { + --primary-color: #2a2a30; + --secondary-color: #3a3a40; + --details-color: #00b1ff; + --margin-h: .8rem; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: sans-serif; +} + +body, +html { + width: 100%; + height: 100%; + font-size: 18px; + background-color: var(--primary-color); +} + +textarea, +button { + border: none; + outline: none; + resize: none; +} + +li { + list-style: none; +} \ No newline at end of file diff --git a/assets/css/index.css b/assets/css/index.css new file mode 100644 index 0000000..1a4078e --- /dev/null +++ b/assets/css/index.css @@ -0,0 +1,188 @@ +.c-container { + display: flex; + flex-direction: column; + width: 100%; + max-width: 600px; + height: 100%; + margin: 0 auto; + padding: var(--margin-h); + padding-right: 0; +} + +/* Header */ + +.c-header { + width: 100%; +} + +.c-header h1 { + /* margin-top: .5rem; */ + color: #efefff; + font-size: 1rem; + text-align: center; +} + +/* MAIN */ + +.c-main { + flex: 1; + width: 100%; + margin-top: 1rem; +} + +/* ANSWERS */ + +.c-answers { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + gap: .5rem; + margin-right: .3rem; + padding-right: var(--margin-h); + /* padding-right: calc(var(--margin-h) - .6rem); */ + overflow-y: auto; +} + +.c-answers::-webkit-scrollbar { + width: .3rem; +} + +.c-answers::-webkit-scrollbar-track { + background: transparent; +} + +.c-answers::-webkit-scrollbar-thumb { + background-color: var(--details-color); + border-radius: 20px; +} + +.placeholder { + text-align: center; + font-size: .8rem; + width: 100%; + color: #afafbf; + margin-top: 1rem; +} + +.question { + display: flex; + align-items: center; + margin-top: .5rem; + color: #efefff; + font-size: .9rem; + padding: .3rem 0; +} + +.question::before { + content: ""; + display: inline-block; + width: .15rem; + min-width: .15rem; + height: 100%; + border-radius: 50px; + background: var(--details-color); + margin-right: .5rem; +} + +.question:nth-child(1) { + margin-top: 0; +} + +.answer { + padding: .5rem; + border-radius: .2rem; + color: #dfdfef; + width: 100%; + white-space: pre-wrap; + font-size: .8rem; + line-height: 1.2rem; + background: var(--secondary-color); + /* background: #121212; */ + word-wrap: break-word; +} + + +/* FOOTER */ + +.c-footer { + margin-top: 1rem; + margin-right: var(--margin-h); +} + +/* STATUS */ + +.c-status { + display: flex; + align-items: center; + justify-content: space-between; + margin-right: var(--margin-h); + margin-bottom: .5rem; +} + +.c-status__progress { + font-size: .8rem; + color: rgba(255, 255, 255, .5); +} + +.c-status__stop { + cursor: pointer; + font-size: .7rem; + padding: .3rem .5rem; + color: #fff; + font-weight: bold; + border-radius: .2rem; + background: var(--details-color); + opacity: 1; + transition: opacity .4s ease; +} + +.c-status__stop.hide { + opacity: 0; +} + +.c-status__stop:hover { + background: #059ddf; +} + +/* QUESTION */ + +.c-form { + display: flex; + align-items: center; + background: var(--secondary-color); + border-radius: .3rem; +} + +.c-form textarea { + flex: 1; + padding: .5rem; + height: 100%; + font-size: .8rem; + color: #babac0; + border-radius: .3rem; + border: 2px solid transparent; + background: transparent; +} + +.c-form textarea:disabled { + opacity: .5; +} + +.c-form button { + cursor: pointer; + padding: .2rem; + border-radius: .2rem; + margin: 0 .3rem 0 .5rem; + background: transparent; + transition: background-color .3s ease; +} + +.c-form button:hover { + background: rgba(0, 0, 0, .2); +} + +.c-form button img { + height: 1.2rem; + height: 1.2rem; +} \ No newline at end of file diff --git a/assets/img/send.png b/assets/img/send.png new file mode 100644 index 0000000000000000000000000000000000000000..c24c714d2d98197bf997c8b024af2f73406170f1 GIT binary patch literal 1033 zcmeAS@N?(olHy`uVBq!ia0vp^5+E$V1|&B;f4Cb+DI|LY`7$t6sWC7#v@kII0tz*} zU|=XUU|@Kaz`$TNgMmT3V9u^U8=wSRlDE4HkOqRE&b#Y@6lZ})WHFFV0OJqYPUnFP z_7YEDSN7*jJiLYsmTW&4GcYh+@pN$vi8%arnxn_$M2X||=a*0OI-#eSm=JkOVUbBo z<8BGgNmiSmrl~vbYGD^uEbG*hm6~x$aZ#So*_Vly=3hiEIR>n>VSmYCQ`&K;dFQWh zB@vgicKVvnW&b~&f8Y22Ht*m6->3in`@w~uv`?BlY*JGT;pTeK(HOUv>zMM9w`}_k z|FN-{<}%OTe+T#HUhd$ttIVVWc(&&?&U#SB>^|{RQGov)b^h&HpM(@bmS=5sdLvs> z+BoMYZw-4^)}`yG*f$oB1Uo)0{ev%i*5_nqep_(cQ-6V(bj*68?Tkl><-wh8v z+;FRE%Tm@OZFhFFn>g0ic3qP{_?Yqdf{yFb```cm{rLK`6CPch?hpS~XYhB#?A!CZ z)q8co++%ObICh0ND{`=fhAL(Rey!M6!};OtN0aj_AFpa|o*DGygSElc)m6VtEar8% zC>cnG$x1L7CRSK5Pq{77eXu`p%O;^Y*N;TR-aXF1*?M?YXlTN$4SV)9J87Re=k(ih zqIS5Eu91;x+PZ6D6Q_zkVBJ$WxpJ=FBD?#3>9?C1I8QQvs4_N~*PbD=$bF*20|wTD z2-mc<2Bwls&DM`PKK{+8RX;sF&B!%j!mkMn7mBo$*Y#QMwFx^mZVxG7o`Fz1|tI_V_gFyT|=V~Lt`rwODh8dZ36=! zsT==oGKz-W{FKbJO57S^nEdpC8Z_WGlw{_n7MCRE7U0&yEuQ}YsE5JR)z4*}Q$iB} Dw(qHM literal 0 HcmV?d00001 diff --git a/src/js/index.js b/assets/js/index.js similarity index 100% rename from src/js/index.js rename to assets/js/index.js diff --git a/index.html b/index.html index 925ae48..a1dc54f 100644 --- a/index.html +++ b/index.html @@ -4,32 +4,37 @@ - - + + + ChatGPT
-
+

ChatGPT

-
+

    Faça uma pergunta para exibir aqui a resposta...

- +
+ +

- - + +
-
+
diff --git a/src/css/index.css b/src/css/index.css deleted file mode 100644 index e69de29..0000000 From fdb23c5757f889475e357cb0f62116ef47cb313b Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Sun, 12 Feb 2023 12:06:49 -0300 Subject: [PATCH 02/22] feat: add event to the form to post the question asked on the page --- assets/css/index.css | 13 +++++++------ assets/js/index.js | 27 +++++++++++++++++++++++++++ index.html | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index 1a4078e..cc72108 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -28,11 +28,12 @@ flex: 1; width: 100%; margin-top: 1rem; + overflow: hidden; } -/* ANSWERS */ +/* responses */ -.c-answers { +.c-responses { display: flex; flex-direction: column; width: 100%; @@ -44,15 +45,15 @@ overflow-y: auto; } -.c-answers::-webkit-scrollbar { +.c-responses::-webkit-scrollbar { width: .3rem; } -.c-answers::-webkit-scrollbar-track { +.c-responses::-webkit-scrollbar-track { background: transparent; } -.c-answers::-webkit-scrollbar-thumb { +.c-responses::-webkit-scrollbar-thumb { background-color: var(--details-color); border-radius: 20px; } @@ -89,7 +90,7 @@ margin-top: 0; } -.answer { +.response { padding: .5rem; border-radius: .2rem; color: #dfdfef; diff --git a/assets/js/index.js b/assets/js/index.js index e69de29..1540e42 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -0,0 +1,27 @@ +const c_responses = document.querySelector('.c-responses') +const placeholder = document.querySelector('.placeholder') +const form = document.querySelector('.c-form') +const questionEntry = document.querySelector('.c-form textarea') + +const handleForm = (e) => { + e.preventDefault() + + const question = questionEntry.value.trim() + + if (!question) return + + placeholder.remove() + + sendQuestion(question) + questionEntry.value = "" +} + +const sendQuestion = (question) => { + c_responses.innerHTML += `

${question}

`; + c_responses.scrollTop = c_responses.scrollHeight; + + + +} + +form.addEventListener('submit', handleForm) \ No newline at end of file diff --git a/index.html b/index.html index a1dc54f..05ba504 100644 --- a/index.html +++ b/index.html @@ -17,7 +17,7 @@

ChatGPT

-
    +

      Faça uma pergunta para exibir aqui a resposta...

From 242deac6972dafc1f42531358095ed48f00404f7 Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Sun, 12 Feb 2023 15:03:51 -0300 Subject: [PATCH 03/22] feat: create function to send question and get answer --- assets/js/index.js | 61 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/assets/js/index.js b/assets/js/index.js index 1540e42..d656887 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -3,6 +3,12 @@ const placeholder = document.querySelector('.placeholder') const form = document.querySelector('.c-form') const questionEntry = document.querySelector('.c-form textarea') +const API_KEY = "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" +const renderingSpeed = 50 +const isPrinting = false +const maxTokens = 2048 +const temperature = 0.6 + const handleForm = (e) => { e.preventDefault() @@ -16,12 +22,63 @@ const handleForm = (e) => { questionEntry.value = "" } -const sendQuestion = (question) => { - c_responses.innerHTML += `

${question}

`; +const createElement = (element, className, textContent) => { + const el = document.createElement(element) + el.classList.add(className) + el.textContent = textContent + return el +} + +const sendQuestion = async (question) => { + const h2 = createElement('h2', 'question', question) + c_responses.appendChild(h2) + c_responses.scrollTop = c_responses.scrollHeight; + const jsonResponse = await fetchAPI(question) + + if (!jsonResponse) return + + if (jsonResponse.error?.message) { + const h2 = createElement('h2', 'error', jsonResponse.error.message) + c_responses.appendChild(h2) + return + } + + if (jsonResponse.choices?.[0].text) { + const text = jsonResponse.choices[0].text + console.log(text) + return + } + + console.log("Sem resposta") +} +const fetchAPI = async (question) => { + try { + const response = await fetch("https://api.openai.com/v1/completions", { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + API_KEY, + }, + body: JSON.stringify({ + model: "text-davinci-003", + prompt: question, + max_tokens: maxTokens, // tamanho da resposta + temperature: temperature, // criatividade na resposta + }), + }) + return response.json() + } catch (error) { + console.log(error) + } finally { + questionEntry.value = ""; + questionEntry.disabled = false; + questionEntry.focus(); + } } form.addEventListener('submit', handleForm) \ No newline at end of file From e5017f1aabe5e94552fc958485dd6c77f2ae218b Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Sun, 12 Feb 2023 15:19:05 -0300 Subject: [PATCH 04/22] feat: add stop request button --- assets/js/index.js | 51 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/assets/js/index.js b/assets/js/index.js index d656887..4f4fc99 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -1,26 +1,17 @@ const c_responses = document.querySelector('.c-responses') const placeholder = document.querySelector('.placeholder') +const progressElem = document.querySelector(".c-status__progress") +const stopBtn = document.querySelector(".c-status__stop") const form = document.querySelector('.c-form') const questionEntry = document.querySelector('.c-form textarea') + const API_KEY = "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" const renderingSpeed = 50 const isPrinting = false const maxTokens = 2048 const temperature = 0.6 - -const handleForm = (e) => { - e.preventDefault() - - const question = questionEntry.value.trim() - - if (!question) return - - placeholder.remove() - - sendQuestion(question) - questionEntry.value = "" -} +let controller = null const createElement = (element, className, textContent) => { const el = document.createElement(element) @@ -35,6 +26,7 @@ const sendQuestion = async (question) => { c_responses.scrollTop = c_responses.scrollHeight; + stopBtn.classList.remove('hide') const jsonResponse = await fetchAPI(question) if (!jsonResponse) return @@ -56,6 +48,9 @@ const sendQuestion = async (question) => { const fetchAPI = async (question) => { try { + controller = new AbortController(); + const signal = controller.signal; + const response = await fetch("https://api.openai.com/v1/completions", { method: "POST", headers: { @@ -69,11 +64,16 @@ const fetchAPI = async (question) => { max_tokens: maxTokens, // tamanho da resposta temperature: temperature, // criatividade na resposta }), + signal }) return response.json() } catch (error) { - console.log(error) + if (error.name === 'AbortError') { + console.log('A requisição foi interrompida.'); + } else { + console.error('Erro ao fazer a requisição:', error); + } } finally { questionEntry.value = ""; questionEntry.disabled = false; @@ -81,4 +81,25 @@ const fetchAPI = async (question) => { } } -form.addEventListener('submit', handleForm) \ No newline at end of file +const handleForm = (e) => { + e.preventDefault() + + const question = questionEntry.value.trim() + + if (!question) return + + placeholder.remove() + + sendQuestion(question) + questionEntry.value = "" +} + +form.addEventListener('submit', handleForm) + +const handleStopRequest = () => { + controller.abort(); + progressElem.textContent = "Requisição parada..."; + stopBtn.classList.add('hide') +} + +stopBtn.addEventListener('click', handleStopRequest) \ No newline at end of file From d82ffa463e35386a69d2c260a6fc7407a3825076 Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Sun, 12 Feb 2023 15:56:33 -0300 Subject: [PATCH 05/22] feat: add functionality to write response --- assets/css/index.css | 8 +++----- assets/js/index.js | 46 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index cc72108..c63ea99 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -103,7 +103,6 @@ word-wrap: break-word; } - /* FOOTER */ .c-footer { @@ -117,7 +116,6 @@ display: flex; align-items: center; justify-content: space-between; - margin-right: var(--margin-h); margin-bottom: .5rem; } @@ -172,7 +170,7 @@ .c-form button { cursor: pointer; - padding: .2rem; + padding: .25rem; border-radius: .2rem; margin: 0 .3rem 0 .5rem; background: transparent; @@ -184,6 +182,6 @@ } .c-form button img { - height: 1.2rem; - height: 1.2rem; + height: 1.1rem; + height: 1.1rem; } \ No newline at end of file diff --git a/assets/js/index.js b/assets/js/index.js index 4f4fc99..b42a538 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -7,10 +7,10 @@ const questionEntry = document.querySelector('.c-form textarea') const API_KEY = "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" -const renderingSpeed = 50 -const isPrinting = false +const renderingSpeed = 40 const maxTokens = 2048 const temperature = 0.6 +let isPrinting = false let controller = null const createElement = (element, className, textContent) => { @@ -27,23 +27,25 @@ const sendQuestion = async (question) => { c_responses.scrollTop = c_responses.scrollHeight; stopBtn.classList.remove('hide') + progressElem.textContent = "Aguardando resposta da api..." + const jsonResponse = await fetchAPI(question) if (!jsonResponse) return if (jsonResponse.error?.message) { - const h2 = createElement('h2', 'error', jsonResponse.error.message) + const h2 = createElement('h2', 'response', jsonResponse.error.message) c_responses.appendChild(h2) return } if (jsonResponse.choices?.[0].text) { const text = jsonResponse.choices[0].text - console.log(text) + writeText(text) return } - console.log("Sem resposta") + writeText("Sem resposta") } const fetchAPI = async (question) => { @@ -81,6 +83,39 @@ const fetchAPI = async (question) => { } } +const sleep = (ms) => new Promise(res => setInterval(res, ms)) + +const writeText = async (text) => { + text = text.replace(/^(\n)+/g, ''); + + const responseElem = createElement('pre', 'response', "Chat GPT:\n\n") + c_responses.appendChild(responseElem) + + isPrinting = true + for (let i = 0; i < text.length; i++) { + const hasText = responseElem.textContent + responseElem.textContent = hasText ? hasText + text[i] : text[i]; + + progressElem.textContent = + `Respondendo [ ${i + 1} / ${text.length} ] ${Math.floor((i + 1) / text.length * 100)}%`; + + c_responses.scrollTop = c_responses.scrollHeight; + + if (!isPrinting) { + progressElem.textContent = "Renderização parada..." + return + } + + await sleep(renderingSpeed) + } + + isPrinting = false + + progressElem.textContent = `[ ${text.length} / ${text.length} ] 100%` + + stopBtn.classList.add('hide') +} + const handleForm = (e) => { e.preventDefault() @@ -97,6 +132,7 @@ const handleForm = (e) => { form.addEventListener('submit', handleForm) const handleStopRequest = () => { + isPrinting = false controller.abort(); progressElem.textContent = "Requisição parada..."; stopBtn.classList.add('hide') From 1a21f037d14ac6745497d5ffc451bc188635a8d8 Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Sun, 12 Feb 2023 18:25:06 -0300 Subject: [PATCH 06/22] feat: add cache to data --- assets/css/index.css | 23 ++++++++++--------- assets/img/menu.png | Bin 0 -> 817 bytes assets/js/index.js | 53 ++++++++++++++++++++++++++++++++++++++++--- index.html | 7 +++--- 4 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 assets/img/menu.png diff --git a/assets/css/index.css b/assets/css/index.css index c63ea99..bdd6311 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -12,13 +12,16 @@ /* Header */ .c-header { - width: 100%; + display: flex; + justify-content: space-between; + align-items: baseline; + padding-right: var(--margin-h); } .c-header h1 { - /* margin-top: .5rem; */ color: #efefff; font-size: 1rem; + width: 100%; text-align: center; } @@ -70,20 +73,18 @@ display: flex; align-items: center; margin-top: .5rem; - color: #efefff; + color: #dadaf0; font-size: .9rem; padding: .3rem 0; } -.question::before { - content: ""; - display: inline-block; - width: .15rem; - min-width: .15rem; - height: 100%; - border-radius: 50px; - background: var(--details-color); +.question img { + height: 1.1rem; + height: 1.1rem; + padding: .2rem; + border-radius: .2rem; margin-right: .5rem; + background: rgba(0, 0, 0, .3); } .question:nth-child(1) { diff --git a/assets/img/menu.png b/assets/img/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd3eb7992d3d5d3c69de9b1bb669b398a08f4ef GIT binary patch literal 817 zcmeAS@N?(olHy`uVBq!ia0vp^5+E$V1|&B;f4Cb+DI|LY`7$t6sWC7#v@kII0tz*} zU|=XUU|@Kaz`$TNgMmT3V9u^U8=wSRlDE4HkOqRE&b#Y@6lZ})WHFFV0OJqYPUnFP z_7YEDSN7*jJR;`&Z$xf;GcYjz@pN$vi8%cBx}i>Xpu~ZX|F56CwCj43+b$Ea&XZ@J zM9pBi>Uk+)5%*S)EJu$J^~oOU+*dtr_)2y2UAoY^)pk4Evfq0TUVi&@>$KoU`{z9^ zFDjb!Uikm9{ef>^UYs;dVRq{D*bg>)FI#i8znqZb(e~i$RLQgj0Sc~M)21DfnZbOu zCN8~HY}xW$=jeSG`rbI$>6fluy~~Fs{CU6y z=`V*~ty*(}YtP=BZGXl8U6*8WWLP<+MouERC#!zHi)vS2Z>Fnu$KxAp<_`lq7ae=i zq^A%b?sx2DN66}JvHNR(yM%^@9_jyS=%G^L|2^RR<3{!lmmKEVt{n>d&Kn#K(6DGe zrywKm{dYy(l3g1QiUe=inR)wWk>{ide(g(CR6W#kt8e+n6eboLrbvH!Dp%Q}z_D_* z-mKF%zipX)Z7bi-n9stqtBd(RT5P{_=m-n1R0jK5xB2sTII-GV7aRGpSz2y9`|IuP z>oU*&?2r$am3T18yQE}Ga{JD%unDiTj~B{JT&}+IYL@BMkA;!bOD$|uyLDf@+Qj=W zJ$uc;-@DsPc_vRzdUkeC*3LD*ckyqWksH7IQw*r3{zz6F>xR7+eV zN>UO_QmvAUQh^kMk%5u1u7Q!Rp;3sDft9JLm7%G&fq|8Q!OAP^KcZ;J%}>cptHiD0 oMyEJ19W!XaZ79jiO)V}-%q_sJr+<#wB%mG!Pgg&ebxsLQ0Fs { return el } +const renderLoadedData = () => { + const tags = questionsAndAnswers.reduce((acc, { question, answer }) => { + answer = answer.replace(/^(\n)+/g, ''); + + const questionTag = createElement('h2', 'question', question) + questionTag.innerHTML = 'Ícone para enviar pergunta' + questionTag.textContent + const responseTag = createElement('pre', 'response', `Chat GPT:\n\n${answer}`) + + acc = [...acc, questionTag, responseTag] + return acc + }, []) + + c_responses.append(...tags) +} + +const loadDataStorage = () => { + const data = JSON.parse(localStorage.getItem("@mr:chatGPT")) || [] + + questionsAndAnswers = data + if (data.length) { + renderLoadedData() + } else { + c_responses.innerHTML = '

Faça uma pergunta para exibir aqui a resposta...

' + } + c_responses.scrollTop = c_responses.scrollHeight; +} +loadDataStorage() + +const saveDataStorage = () => { + const dataJson = JSON.stringify(questionsAndAnswers) + + localStorage.setItem("@mr:chatGPT", dataJson) +} + const sendQuestion = async (question) => { const h2 = createElement('h2', 'question', question) + h2.innerHTML = 'Ícone para enviar pergunta' + h2.textContent + c_responses.appendChild(h2) c_responses.scrollTop = c_responses.scrollHeight; @@ -41,7 +80,15 @@ const sendQuestion = async (question) => { if (jsonResponse.choices?.[0].text) { const text = jsonResponse.choices[0].text + writeText(text) + + questionsAndAnswers.push({ + question, + answer: text + }) + saveDataStorage() + return } @@ -96,7 +143,7 @@ const writeText = async (text) => { const hasText = responseElem.textContent responseElem.textContent = hasText ? hasText + text[i] : text[i]; - progressElem.textContent = + progressElem.textContent = `Respondendo [ ${i + 1} / ${text.length} ] ${Math.floor((i + 1) / text.length * 100)}%`; c_responses.scrollTop = c_responses.scrollHeight; @@ -108,7 +155,7 @@ const writeText = async (text) => { await sleep(renderingSpeed) } - + isPrinting = false progressElem.textContent = `[ ${text.length} / ${text.length} ] 100%` @@ -123,7 +170,7 @@ const handleForm = (e) => { if (!question) return - placeholder.remove() + placeholder && placeholder.remove() sendQuestion(question) questionEntry.value = "" diff --git a/index.html b/index.html index 05ba504..42d9cc8 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,6 @@ + @@ -17,11 +18,9 @@

ChatGPT

-
    -

    Faça uma pergunta para exibir aqui a resposta...

    -
+
- +

From 7f3016cceeef4e73b7a691cdf6faa3900d381133 Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Wed, 15 Feb 2023 00:08:37 -0300 Subject: [PATCH 07/22] feat(settings): add settings for application control --- assets/css/global.css | 5 +- assets/css/index.css | 148 +++++++++++++++++++++++++++++++++++++++-- assets/js/index.js | 150 +++++++++++++++++++++++++++++++++++++----- index.html | 40 +++++++++++ 4 files changed, 319 insertions(+), 24 deletions(-) diff --git a/assets/css/global.css b/assets/css/global.css index 8be1361..36cb2d5 100644 --- a/assets/css/global.css +++ b/assets/css/global.css @@ -3,9 +3,10 @@ --secondary-color: #3a3a40; --details-color: #00b1ff; --margin-h: .8rem; + --font-size: 18px; } -* { +*, *::before { margin: 0; padding: 0; box-sizing: border-box; @@ -16,7 +17,7 @@ body, html { width: 100%; height: 100%; - font-size: 18px; + font-size: var(--font-size); background-color: var(--primary-color); } diff --git a/assets/css/index.css b/assets/css/index.css index bdd6311..96396a3 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -18,11 +18,18 @@ padding-right: var(--margin-h); } +.c-header__menu { + display: flex; +} + +.c-header__menu img { + width: 1rem; + height: 1rem; +} + .c-header h1 { color: #efefff; font-size: 1rem; - width: 100%; - text-align: center; } /* MAIN */ @@ -43,8 +50,8 @@ height: 100%; gap: .5rem; margin-right: .3rem; - padding-right: var(--margin-h); - /* padding-right: calc(var(--margin-h) - .6rem); */ + /* padding-right: var(--margin-h); */ + padding-right: calc(var(--margin-h) - .3rem); overflow-y: auto; } @@ -81,10 +88,10 @@ .question img { height: 1.1rem; height: 1.1rem; - padding: .2rem; + padding: .1rem; border-radius: .2rem; margin-right: .5rem; - background: rgba(0, 0, 0, .3); + /* background: rgba(255, 255, 255, .09); */ } .question:nth-child(1) { @@ -152,6 +159,12 @@ align-items: center; background: var(--secondary-color); border-radius: .3rem; + border: 2px solid transparent; + transition: border .3s; +} + +.c-form:focus-within { + border: 2px solid var(--details-color); } .c-form textarea { @@ -185,4 +198,127 @@ .c-form button img { height: 1.1rem; height: 1.1rem; +} + +/* SETTINGS */ + +.c-blur { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: none; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, .5); +} + +.c-blur.active { + display: flex; + align-items: center; + justify-content: center; +} + +.c-settings { + display: flex; + flex-direction: column; + padding: 1rem; + border-radius: .5rem; + background: var(--secondary-color); + box-shadow: 0 2px 1px 1px rgba(0, 0, 0, .2); +} + + +.c-settings h2 { + font-size: .8rem; + text-align: center; + color: #efefff; +} + +.c-settings__control { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + list-style: none; + margin-top: 1rem; +} + +.c-settings__control label { + color: #b2b1b5; + white-space: nowrap; + margin-right: 1rem; + font-size: .8rem; +} + +.c-settings__control input[type=number] { + padding: .3rem .2rem; + border: 1px solid rgba(255, 255, 255, .1); + color: #efefff; + font-size: .6rem; + background: rgba(0, 0, 0, .15); + margin-left: .5rem; +} + +.c-settings__control input[type='checkbox'] { + display: flex; + align-items: center; + justify-content: center; + position: relative; + appearance: none; + width: 1rem; + height: 1rem; + border-radius: .2rem; + background: var(--primary-color); +} + +.c-settings__control input[type='checkbox']:checked::before { + content: ""; + position: absolute; + border-left: .18rem solid var(--details-color); + border-bottom: .18rem solid var(--details-color); + display: inline-block; + width: .5rem; + height: .4rem; + margin-top: -.2rem; + transform: rotate(-45deg); +} + +.c-settings__control [data-temperature-value] { + font-size: .8rem; + color: #dadafa; + margin-left: .5rem; +} + +.c-settings .buttons { + display: flex; + align-items: center; +} + +.c-settings button { + padding: .4rem .5rem; + flex: 1; + margin-top: 1rem; + font-size: .6rem; + font-weight: bold; + color: #efefff; + cursor: pointer; + border-radius: .2rem; + background: rgba(255, 255, 255, .1); + opacity: .9; +} + +.c-settings button:last-child { + margin-left: .5rem; + color: rgba(255, 255, 255, .9); + background: var(--details-color); +} + +.c-settings button:hover { + opacity: 1; +} + +.c-settings button:active { + transform: scale(.95) } \ No newline at end of file diff --git a/assets/js/index.js b/assets/js/index.js index 7e2a9de..13ae1df 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -1,18 +1,24 @@ -const c_responses = document.querySelector('.c-responses') +const root = document.documentElement +const menu = document.querySelector('[data-menu]') +const blur = document.querySelector('[data-settings-blur]') +const responses = document.querySelector('.c-responses') const placeholder = document.querySelector('.placeholder') const progressElem = document.querySelector(".c-status__progress") const stopBtn = document.querySelector(".c-status__stop") const form = document.querySelector('.c-form') const questionEntry = document.querySelector('.c-form textarea') +const settingsForm = document.querySelector('[data-settings]') +const inputsSettings = settingsForm.querySelectorAll('input') +const temperatureValue = document.querySelector('[data-temperature-value]') +const closeBtn = document.querySelector('[data-settings-close]') + let currentQuestion = null let currentAnswer = null let questionsAndAnswers = [] +let settings = {} const API_KEY = "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" -const renderingSpeed = 40 -const maxTokens = 2048 -const temperature = 0.6 let isPrinting = false let controller = null @@ -35,7 +41,7 @@ const renderLoadedData = () => { return acc }, []) - c_responses.append(...tags) + responses.append(...tags) } const loadDataStorage = () => { @@ -45,12 +51,48 @@ const loadDataStorage = () => { if (data.length) { renderLoadedData() } else { - c_responses.innerHTML = '

Faça uma pergunta para exibir aqui a resposta...

' + responses.innerHTML = '

Faça uma pergunta para exibir aqui a resposta...

' } - c_responses.scrollTop = c_responses.scrollHeight; + responses.scrollTop = responses.scrollHeight; } loadDataStorage() +const initialSettings = { + font_size: 18, + max_tokens: 2050, + rendering_speed: 40, + temperature: 0.6, + save_context, + save_queries +} + +const loadSettingsStorage = () => { + settings = JSON.parse(localStorage.getItem("@mr:chatGPT:settings")) || initialSettings + + const keys = Object.keys(settings) + keys.forEach(key => { + const isCheckbox = typeof settings[key] === 'boolean' + if (isCheckbox) { + document.getElementById(key).checked = settings[key] + return + } + + // number and radio input + const value = settings[key] + document.getElementById(key).value = value + + if (key === 'temperature') { + const temperature = value + temperatureValue.textContent = temperature.toFixed(1) + } + + if (key === 'font_size') { + root.style.setProperty('--font-size', `${value}px`) + } + }) +} +loadSettingsStorage() + const saveDataStorage = () => { const dataJson = JSON.stringify(questionsAndAnswers) @@ -61,9 +103,9 @@ const sendQuestion = async (question) => { const h2 = createElement('h2', 'question', question) h2.innerHTML = 'Ícone para enviar pergunta' + h2.textContent - c_responses.appendChild(h2) + responses.appendChild(h2) - c_responses.scrollTop = c_responses.scrollHeight; + responses.scrollTop = responses.scrollHeight; stopBtn.classList.remove('hide') progressElem.textContent = "Aguardando resposta da api..." @@ -74,7 +116,7 @@ const sendQuestion = async (question) => { if (jsonResponse.error?.message) { const h2 = createElement('h2', 'response', jsonResponse.error.message) - c_responses.appendChild(h2) + responses.appendChild(h2) return } @@ -110,8 +152,8 @@ const fetchAPI = async (question) => { body: JSON.stringify({ model: "text-davinci-003", prompt: question, - max_tokens: maxTokens, // tamanho da resposta - temperature: temperature, // criatividade na resposta + max_tokens: settings.max_tokens, // tamanho da resposta + temperature: settings.temperature, // criatividade na resposta }), signal }) @@ -136,7 +178,7 @@ const writeText = async (text) => { text = text.replace(/^(\n)+/g, ''); const responseElem = createElement('pre', 'response', "Chat GPT:\n\n") - c_responses.appendChild(responseElem) + responses.appendChild(responseElem) isPrinting = true for (let i = 0; i < text.length; i++) { @@ -146,14 +188,14 @@ const writeText = async (text) => { progressElem.textContent = `Respondendo [ ${i + 1} / ${text.length} ] ${Math.floor((i + 1) / text.length * 100)}%`; - c_responses.scrollTop = c_responses.scrollHeight; + responses.scrollTop = responses.scrollHeight; if (!isPrinting) { progressElem.textContent = "Renderização parada..." return } - await sleep(renderingSpeed) + await sleep(settings.rendering_speed) } isPrinting = false @@ -163,6 +205,12 @@ const writeText = async (text) => { stopBtn.classList.add('hide') } +const showHideBlurSettings = () => { + blur.classList.toggle('active') +} + +menu.addEventListener('click', showHideBlurSettings) + const handleForm = (e) => { e.preventDefault() @@ -185,4 +233,74 @@ const handleStopRequest = () => { stopBtn.classList.add('hide') } -stopBtn.addEventListener('click', handleStopRequest) \ No newline at end of file +stopBtn.addEventListener('click', handleStopRequest) + +const handleSettingsForm = (e) => { + e.preventDefault() + + const formData = new FormData(e.target) + const { + font_size, + max_tokens, + rendering_speed, + temperature, + save_context, + save_queries + } = Object.fromEntries(formData) + + const updatedData = { + font_size: Number(font_size), + max_tokens: Number(max_tokens), + rendering_speed: Number(rendering_speed), + temperature: Number(temperature), + save_context: !!save_context, + save_queries: !!save_queries + } + + settings = updatedData + + const settingsJson = JSON.stringify(updatedData) + + localStorage.setItem("@mr:chatGPT:settings", settingsJson) + + showHideBlurSettings() +} + + +settingsForm.addEventListener('submit', handleSettingsForm) + +const handleInputsSettings = ({ target: el }) => { + const name = el.getAttribute('name') + + const allowedSettings = { + font_size: () => { + const size = el.value + root.style.setProperty('--font-size', `${size}px`) + }, + temperature: () => { + const newValue = Number(el.value) + temperatureValue.textContent = newValue.toFixed(1) + }, + save_context: () => { + el.checked && console.log('O contexto das do chat será enviado nas requisições futuras.') + }, + save_queries: () => { + el.checked && console.log('As consultas serão salvas.') + } + } + + const fn = allowedSettings[name] + fn && fn() +} + +inputsSettings.forEach(input => + input.addEventListener('change', handleInputsSettings)) + +const handleCloseClick = (e) => { + e.preventDefault() + + loadSettingsStorage() + showHideBlurSettings() +} + +closeBtn.addEventListener('click', handleCloseClick); \ No newline at end of file diff --git a/index.html b/index.html index 42d9cc8..764ca14 100644 --- a/index.html +++ b/index.html @@ -13,8 +13,48 @@
+
+
+

Configurações

+ +
+ + +
+
+ + +
+
+ + +
+
+ + + 0.6 +
+
+ + +
+
+ + +
+
+ + +
+
+
+

ChatGPT

+ + + Imagem de menu +
From fed17a99bbc14898488ac61b7497c450b463a84b Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Wed, 15 Feb 2023 00:30:30 -0300 Subject: [PATCH 08/22] fix: handle errors when the api returns an error message --- assets/css/index.css | 4 ++++ assets/js/index.js | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index 96396a3..d06ba33 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -195,6 +195,10 @@ background: rgba(0, 0, 0, .2); } +.c-form button:focus { + border: 2px solid rgba(255, 255, 255, .1); +} + .c-form button img { height: 1.1rem; height: 1.1rem; diff --git a/assets/js/index.js b/assets/js/index.js index 13ae1df..3ec03e0 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -2,7 +2,6 @@ const root = document.documentElement const menu = document.querySelector('[data-menu]') const blur = document.querySelector('[data-settings-blur]') const responses = document.querySelector('.c-responses') -const placeholder = document.querySelector('.placeholder') const progressElem = document.querySelector(".c-status__progress") const stopBtn = document.querySelector(".c-status__stop") const form = document.querySelector('.c-form') @@ -12,6 +11,7 @@ const settingsForm = document.querySelector('[data-settings]') const inputsSettings = settingsForm.querySelectorAll('input') const temperatureValue = document.querySelector('[data-temperature-value]') const closeBtn = document.querySelector('[data-settings-close]') +let placeholder = null let currentQuestion = null let currentAnswer = null @@ -51,7 +51,8 @@ const loadDataStorage = () => { if (data.length) { renderLoadedData() } else { - responses.innerHTML = '

Faça uma pergunta para exibir aqui a resposta...

' + placeholder = createElement('p', 'placeholder', 'Faça uma pergunta para exibir aqui a resposta...') + responses.appendChild(placeholder) } responses.scrollTop = responses.scrollHeight; } @@ -115,8 +116,12 @@ const sendQuestion = async (question) => { if (!jsonResponse) return if (jsonResponse.error?.message) { + progressElem.textContent = 'Erro ao fazer consulta, tente mais tarde.' + stopBtn.classList.add('hide') + const h2 = createElement('h2', 'response', jsonResponse.error.message) responses.appendChild(h2) + responses.scrollTop = responses.scrollHeight; return } @@ -163,8 +168,10 @@ const fetchAPI = async (question) => { if (error.name === 'AbortError') { console.log('A requisição foi interrompida.'); } else { + progressElem.textContent = 'Erro ao fazer a requisição, tente mais tarde.' console.error('Erro ao fazer a requisição:', error); } + stopBtn.classList.add('hide') } finally { questionEntry.value = ""; questionEntry.disabled = false; From 59cfdc91154d690effaf11ac49d75528b381d0aa Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Wed, 15 Feb 2023 01:29:52 -0300 Subject: [PATCH 09/22] style: header layout changes --- assets/css/global.css | 3 +- assets/css/index.css | 30 +++++++++++++---- assets/js/index.js | 8 ++--- index.html | 76 +++++++++++++++++++++---------------------- 4 files changed, 68 insertions(+), 49 deletions(-) diff --git a/assets/css/global.css b/assets/css/global.css index 36cb2d5..104af24 100644 --- a/assets/css/global.css +++ b/assets/css/global.css @@ -18,7 +18,8 @@ html { width: 100%; height: 100%; font-size: var(--font-size); - background-color: var(--primary-color); + /* background-color: #222225; */ + background: var(--primary-color); } textarea, diff --git a/assets/css/index.css b/assets/css/index.css index d06ba33..732c7bb 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -5,8 +5,8 @@ max-width: 600px; height: 100%; margin: 0 auto; - padding: var(--margin-h); - padding-right: 0; + padding-bottom: var(--margin-h); + background: var(--primary-color); } /* Header */ @@ -15,7 +15,8 @@ display: flex; justify-content: space-between; align-items: baseline; - padding-right: var(--margin-h); + padding: .5rem var(--margin-h); + background: var(--secondary-color); } .c-header__menu { @@ -32,6 +33,24 @@ font-size: 1rem; } +.c-header__new_chat { + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 1.3rem; + height: 1.3rem; + border-radius: .15rem; + font-size: 1rem; + color: #dfdfef; + font-weight: bold; + background: rgba(255, 255, 255, .1); +} + +.c-header__new_chat:hover { + background: rgba(255, 255, 255, .2); +} + /* MAIN */ .c-main { @@ -46,11 +65,10 @@ .c-responses { display: flex; flex-direction: column; - width: 100%; height: 100%; gap: .5rem; margin-right: .3rem; - /* padding-right: var(--margin-h); */ + margin-left: var(--margin-h); padding-right: calc(var(--margin-h) - .3rem); overflow-y: auto; } @@ -114,8 +132,8 @@ /* FOOTER */ .c-footer { + margin: 0 var(--margin-h); margin-top: 1rem; - margin-right: var(--margin-h); } /* STATUS */ diff --git a/assets/js/index.js b/assets/js/index.js index 3ec03e0..4f127a5 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -6,12 +6,12 @@ const progressElem = document.querySelector(".c-status__progress") const stopBtn = document.querySelector(".c-status__stop") const form = document.querySelector('.c-form') const questionEntry = document.querySelector('.c-form textarea') +let placeholderElem = null const settingsForm = document.querySelector('[data-settings]') const inputsSettings = settingsForm.querySelectorAll('input') const temperatureValue = document.querySelector('[data-temperature-value]') const closeBtn = document.querySelector('[data-settings-close]') -let placeholder = null let currentQuestion = null let currentAnswer = null @@ -51,8 +51,8 @@ const loadDataStorage = () => { if (data.length) { renderLoadedData() } else { - placeholder = createElement('p', 'placeholder', 'Faça uma pergunta para exibir aqui a resposta...') - responses.appendChild(placeholder) + placeholderElem = createElement('p', 'placeholder', 'Faça uma pergunta para exibir aqui a resposta...') + responses.appendChild(placeholderElem) } responses.scrollTop = responses.scrollHeight; } @@ -225,7 +225,7 @@ const handleForm = (e) => { if (!question) return - placeholder && placeholder.remove() + placeholderElem && placeholderElem.remove() sendQuestion(question) questionEntry.value = "" diff --git a/index.html b/index.html index 764ca14..7b89327 100644 --- a/index.html +++ b/index.html @@ -13,48 +13,12 @@
-
-
-

Configurações

- -
- - -
-
- - -
-
- - -
-
- - - 0.6 -
-
- - -
-
- - -
-
- - -
-
-
-
-

ChatGPT

- Imagem de menu +

ChatGPT

+
@@ -75,6 +39,42 @@

ChatGPT

+ +
+
+

Configurações

+ +
+ + +
+
+ + +
+
+ + +
+
+ + + 0.6 +
+
+ + +
+
+ + +
+
+ + +
+
+
\ No newline at end of file From dd192ecaa0561686f8b4ace03544890d3862dc41 Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Wed, 15 Feb 2023 11:50:33 -0300 Subject: [PATCH 10/22] refactor(settings): change how to set text size in settings --- assets/css/index.css | 7 ++++++- assets/js/index.js | 3 +++ index.html | 17 +++++++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index 732c7bb..1aff3ea 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -267,6 +267,11 @@ margin-top: 1rem; } +.c-settings__control > div { + display: flex; + align-items: center; +} + .c-settings__control label { color: #b2b1b5; white-space: nowrap; @@ -307,7 +312,7 @@ transform: rotate(-45deg); } -.c-settings__control [data-temperature-value] { +.c-settings__control span { font-size: .8rem; color: #dadafa; margin-left: .5rem; diff --git a/assets/js/index.js b/assets/js/index.js index 4f127a5..10c617c 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -12,6 +12,7 @@ const settingsForm = document.querySelector('[data-settings]') const inputsSettings = settingsForm.querySelectorAll('input') const temperatureValue = document.querySelector('[data-temperature-value]') const closeBtn = document.querySelector('[data-settings-close]') +const fontSizeValue = document.querySelector('[data-font_size-value]') let currentQuestion = null let currentAnswer = null @@ -89,6 +90,7 @@ const loadSettingsStorage = () => { if (key === 'font_size') { root.style.setProperty('--font-size', `${value}px`) + fontSizeValue.textContent = value } }) } @@ -283,6 +285,7 @@ const handleInputsSettings = ({ target: el }) => { font_size: () => { const size = el.value root.style.setProperty('--font-size', `${size}px`) + fontSizeValue.textContent = size }, temperature: () => { const newValue = Number(el.value) diff --git a/index.html b/index.html index 7b89327..2f0c4e3 100644 --- a/index.html +++ b/index.html @@ -46,7 +46,17 @@

Configurações

- +
+ + 18 +
+
+
+ +
+ + 0.6 +
@@ -56,11 +66,6 @@

Configurações

-
- - - 0.6 -
From 2e8d8cc8af70fd098dad70660a144bbd70ce844b Mon Sep 17 00:00:00 2001 From: Lucca Santos Date: Wed, 15 Feb 2023 12:45:34 -0300 Subject: [PATCH 11/22] feat: apply logic to save queries and include context in requests when checked in settings --- assets/css/index.css | 4 ++ assets/js/index.js | 107 ++++++++++++++++++++++++------------------- index.html | 4 +- 3 files changed, 68 insertions(+), 47 deletions(-) diff --git a/assets/css/index.css b/assets/css/index.css index 1aff3ea..aef022e 100644 --- a/assets/css/index.css +++ b/assets/css/index.css @@ -129,6 +129,10 @@ word-wrap: break-word; } +.response.error { + border: 2px solid rgb(211, 13, 128); +} + /* FOOTER */ .c-footer { diff --git a/assets/js/index.js b/assets/js/index.js index 10c617c..f8be75f 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -6,7 +6,7 @@ const progressElem = document.querySelector(".c-status__progress") const stopBtn = document.querySelector(".c-status__stop") const form = document.querySelector('.c-form') const questionEntry = document.querySelector('.c-form textarea') -let placeholderElem = null +const placeholderElem = document.querySelector('placeholder') const settingsForm = document.querySelector('[data-settings]') const inputsSettings = settingsForm.querySelectorAll('input') @@ -23,49 +23,13 @@ const API_KEY = "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" let isPrinting = false let controller = null -const createElement = (element, className, textContent) => { - const el = document.createElement(element) - el.classList.add(className) - el.textContent = textContent - return el -} - -const renderLoadedData = () => { - const tags = questionsAndAnswers.reduce((acc, { question, answer }) => { - answer = answer.replace(/^(\n)+/g, ''); - - const questionTag = createElement('h2', 'question', question) - questionTag.innerHTML = 'Ícone para enviar pergunta' + questionTag.textContent - const responseTag = createElement('pre', 'response', `Chat GPT:\n\n${answer}`) - - acc = [...acc, questionTag, responseTag] - return acc - }, []) - - responses.append(...tags) -} - -const loadDataStorage = () => { - const data = JSON.parse(localStorage.getItem("@mr:chatGPT")) || [] - - questionsAndAnswers = data - if (data.length) { - renderLoadedData() - } else { - placeholderElem = createElement('p', 'placeholder', 'Faça uma pergunta para exibir aqui a resposta...') - responses.appendChild(placeholderElem) - } - responses.scrollTop = responses.scrollHeight; -} -loadDataStorage() - const initialSettings = { font_size: 18, max_tokens: 2050, rendering_speed: 40, temperature: 0.6, - save_context, - save_queries + save_context: false, + save_queries: true } const loadSettingsStorage = () => { @@ -96,6 +60,47 @@ const loadSettingsStorage = () => { } loadSettingsStorage() +const createElement = (element, className, textContent) => { + const el = document.createElement(element) + + if (className.includes(' ')) { + className.split(' ').forEach(c => el.classList.add(c)) + } else { + el.classList.add(className) + } + + el.textContent = textContent + return el +} + +const renderLoadedData = () => { + placeholderElem && placeholderElem.remove() + + const tags = questionsAndAnswers.reduce((acc, { question, answer }) => { + answer = answer.replace(/^(\n)+/g, ''); + + const questionTag = createElement('h2', 'question', question) + questionTag.innerHTML = 'Ícone para enviar pergunta' + questionTag.textContent + const responseTag = createElement('pre', 'response', `Chat GPT:\n\n${answer}`) + + acc = [...acc, questionTag, responseTag] + return acc + }, []) + + responses.append(...tags) +} + +const loadDataStorage = () => { + const data = JSON.parse(localStorage.getItem("@mr:chatGPT")) || [] + + questionsAndAnswers = data + + renderLoadedData() + + responses.scrollTop = responses.scrollHeight; +} +settings.save_queries && loadDataStorage() + const saveDataStorage = () => { const dataJson = JSON.stringify(questionsAndAnswers) @@ -132,11 +137,15 @@ const sendQuestion = async (question) => { writeText(text) - questionsAndAnswers.push({ - question, - answer: text - }) - saveDataStorage() + if (settings.save_queries) { + questionsAndAnswers.push({ + question, + answer: text + }) + saveDataStorage() + console.log('Salvar queries') + } + return } @@ -146,6 +155,10 @@ const sendQuestion = async (question) => { const fetchAPI = async (question) => { try { + const context = settings.save_context + ? questionsAndAnswers.map(({ answer }) => answer)?.join('') + : '' + controller = new AbortController(); const signal = controller.signal; @@ -158,7 +171,7 @@ const fetchAPI = async (question) => { }, body: JSON.stringify({ model: "text-davinci-003", - prompt: question, + prompt: question + context, max_tokens: settings.max_tokens, // tamanho da resposta temperature: settings.temperature, // criatividade na resposta }), @@ -186,7 +199,9 @@ const sleep = (ms) => new Promise(res => setInterval(res, ms)) const writeText = async (text) => { text = text.replace(/^(\n)+/g, ''); - const responseElem = createElement('pre', 'response', "Chat GPT:\n\n") + const classResponse = text === 'Sem resposta' ? 'response error' : 'response' + + const responseElem = createElement('pre', classResponse, "Chat GPT:\n\n") responses.appendChild(responseElem) isPrinting = true diff --git a/index.html b/index.html index 2f0c4e3..dedb89c 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,9 @@

ChatGPT

-
+
+

Faça uma pergunta para exibir aqui a resposta...

+