From 4976505da7dc60f137c70ce55d15dc685b397b67 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Thu, 11 Jul 2024 16:33:33 +1000 Subject: [PATCH 01/34] add basic account page style Signed-off-by: cbh778899 --- components/account-page/index.js | 93 +++++++++++++++++++++++++++++- components/sidebar.js | 3 + index.html | 2 +- index.js | 2 - styles/account_page.css | 98 ++++++++++++++++++++++++++++++-- tools/svgs.js | 13 +++++ 6 files changed, 200 insertions(+), 11 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index 6354571..0dd8b0d 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -1,6 +1,93 @@ -import getSVG from "../../tools/svgs.js"; +import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js"; + +let account_dialog; + +function toggleDialog(force = null) { + if(!account_dialog) return false; + + let open_status = force === null ? !account_dialog.open : force; + open_status ? account_dialog.showModal() : account_dialog.close(); + + return true; +} + +const account_fields = { + login: [ + { index: 'username' }, + { index: 'password', type: 'password' }, + ], + register: [ + { index: 'username' }, + { index: 'email' }, + { index: 'password', type: 'password' }, + { index: 'repeat-password', title: 'Repeat Password', type: 'password' } + ], + logged_in: [ + { index: 'username', title: 'Update Username' }, + { index: 'email', title: 'Update Email' }, + { index: 'password', title: 'Update Password', type: 'password' }, + ] +} export default function createAccountPage() { - document.getElementById('user-avatar') - .insertAdjacentHTML("afterbegin", getSVG('person-fill')) + if(toggleDialog()) return; + + account_dialog = document.createElement('dialog'); + account_dialog.onclick = () => toggleDialog(false); + document.getElementById('user-popup-container').appendChild(account_dialog) + + const account_main_page = document.createElement('form'); + account_main_page.className = 'account-main'; + account_main_page.onclick = evt => evt.stopPropagation(); + + account_main_page.insertAdjacentHTML("afterbegin", ` +
`) + + const input_main = document.createElement('div'); + input_main.className = 'input-details-main'; + + // TODO: if logged in + account_fields.login.forEach(e=>{ + input_main.appendChild(createAccountInputFields(e)) + }) + + input_main.insertAdjacentHTML("beforeend", "
") + + const submit_button = document.createElement('button') + submit_button.type = 'submit' + submit_button.className = 'submit-button clickable' + submit_button.textContent = 'Login' + input_main.appendChild(submit_button) + + const switch_btn = document.createElement('button') + switch_btn.type = 'submit' + switch_btn.className = 'submit-button reverse-color clickable' + switch_btn.textContent = 'I want to register' + input_main.appendChild(switch_btn) + + account_main_page.appendChild(input_main) + account_dialog.appendChild(account_main_page) + + toggleDialog(); +} + +function createAccountInputFields({index, title = null, type = null}) { + const field_container = document.createElement('div'); + field_container.className = 'account-field-container'; + + const title_element = document.createElement('div'); + title_element.textContent = title || capitalizeFirstLetter(index); + title_element.className = 'title' + + const input = document.createElement('input'); + input.type = type || 'text'; + + field_container.appendChild(title_element); + field_container.appendChild(input); + + if(type === 'password') { + // TODO + } + + return field_container; } \ No newline at end of file diff --git a/components/sidebar.js b/components/sidebar.js index 1074246..b9acafd 100644 --- a/components/sidebar.js +++ b/components/sidebar.js @@ -1,10 +1,12 @@ import createInfoPage from "./info-page.js"; import getSVG from "../tools/svgs.js"; +import createAccountPage from "./account-page/index.js"; const pages = [ { name: 'chat', svgName: 'chat-dots-fill' }, { name: 'model-hero', svgName: 'hero' }, { name: 'training' }, + { name: 'account', svgName: 'person-fill', not_page: true }, { name: 'info', svgName: 'info-circle-fill', not_page: true } ] @@ -27,6 +29,7 @@ export default function createSideBar(switchSelectedPage) { () => switchSelectedPage(name); }) createInfoPage(); + document.getElementById('sidebar-icon-account').onclick = createAccountPage; switchSelectedPage(pages[0].name); } \ No newline at end of file diff --git a/index.html b/index.html index 565535b..c161484 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@ -
+
\ No newline at end of file diff --git a/index.js b/index.js index ee4d14f..b168d9c 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,5 @@ import createSideBar from "./components/sidebar.js"; import createChatPage from "./components/chat-page/index.js"; -import createAccountPage from "./components/account-page/index.js"; import createTrainingPage from "./components/training-page/index.js"; import createModelHeroPage from "./components/model-hero-page/index.js"; @@ -33,7 +32,6 @@ function switchSelectedPage(page) { function build() { createSideBar(switchSelectedPage); - createAccountPage(); } window.onload = build; \ No newline at end of file diff --git a/styles/account_page.css b/styles/account_page.css index 492fa78..6f3d6ae 100644 --- a/styles/account_page.css +++ b/styles/account_page.css @@ -1,7 +1,95 @@ -#user-avatar svg { - width: 110%; - height: 110%; - left: -5%; - bottom: -5%; +.account-main { + --img-width: 60%; + --details-width: calc(100% - var(--img-width)); + + width: 70vw; + height: 80vh; + position: absolute; + display: block; + box-sizing: border-box; + left: 15vw; + top: 10vh; + background-color: white; + border-radius: 15px; +} + +.account-main .logo-image { + position: absolute; + width: var(--img-width); + height: 100%; + left: 0; + top: 0; + align-items: center; + display: flex; +} + +.account-main .logo-image img { + margin: auto; +} + +.account-main .input-details-main { + --item-width: 230px; + --item-height: 40px; + + position: absolute; + width: var(--details-width); + height: 100%; + top: 0; + right: 0; + padding: 30px; + align-content: center; +} + +.input-details-main .account-field-container { + width: fit-content; + height: fit-content; position: relative; + margin-bottom: 10px; +} + +.input-details-main .account-field-container .title { + font-size: 18px; + margin-bottom: 5px; + font-weight: bold; +} + +.input-details-main .account-field-container input { + width: var(--item-width); + height: var(--item-height); + border: 1px solid gray; + border-radius: 5px; + box-sizing: border-box; + padding: 0px 15px; + font-size: 16px; +} +.input-details-main .account-field-container input:focus { + outline: none; +} + +.input-details-main hr { + width: var(--item-width); + position: relative; + margin: auto; + margin-left: 0; + margin-top: 20px; +} + +.input-details-main .submit-button { + width: var(--item-width); + height: calc(var(--item-height) + 5px); + border-radius: 16px; + background-color: dodgerblue; + color: white; + box-sizing: border-box; + border: none; + font-size: 18px; + font-weight: bold; + margin-top: 20px; + display: block; +} + +.input-details-main .submit-button.reverse-color { + background-color: white; + border: 1px solid dodgerblue; + color: dodgerblue; } \ No newline at end of file diff --git a/tools/svgs.js b/tools/svgs.js index c329da5..c57e3ae 100644 --- a/tools/svgs.js +++ b/tools/svgs.js @@ -106,6 +106,19 @@ const svgs = { + `, + + "eye": ` + + + + `, + + "eye-slash": ` + + + + ` } From a6c619e22db36284de6a3f432e9e3778ad94b811 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Thu, 11 Jul 2024 21:25:03 +1000 Subject: [PATCH 02/34] scroll to bottom when switch session Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index e204567..ae2307e 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -201,6 +201,10 @@ function updateConversation() { conversation.history.forEach(({type, message})=>{ main_elem.appendChild(createBlock(type, message)[0]) }) + main_elem.scrollTo({ + top: main_elem.scrollHeight, + behavior: 'smooth' + }) } function createBlock(type, msg = '') { From c4b817bcab0299ae9938093432f873712cb863bc Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Thu, 11 Jul 2024 21:48:15 +1000 Subject: [PATCH 03/34] rename type: out->user; in->assistant Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 10 +++++----- styles/chat_page.css | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index ae2307e..c98ec36 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -131,12 +131,12 @@ async function sendMessage(message, send) { main_elem.innerHTML = '' updateHistoryName(conversation.id, message.substring(0, 20)) } - main_elem.appendChild(createBlock('out', message)[0]); + main_elem.appendChild(createBlock('user', message)[0]); main_elem.scrollTo({ top: main_elem.scrollHeight, behavior: 'smooth' }) - const [bot_answer, updateMessage] = createBlock('in'); + const [bot_answer, updateMessage] = createBlock('assistant'); main_elem.appendChild(bot_answer); const response = await request('chat', { @@ -151,8 +151,8 @@ async function sendMessage(message, send) { togglePending(); appendConversationMessage([ - { type: 'out', message }, - { type: 'in', message: content} + { type: 'user', message }, + { type: 'assistant', message: content} ], conversation.id) } @@ -216,7 +216,7 @@ function createBlock(type, msg = '') { block.appendChild(message); - if(type === 'in') { + if(type === 'assistant') { message.innerHTML = ` ${getSVG('circle-fill', 'dot-animation dot-1')} ${getSVG('circle-fill', 'dot-animation dot-2')} diff --git a/styles/chat_page.css b/styles/chat_page.css index c4fac35..017790a 100644 --- a/styles/chat_page.css +++ b/styles/chat_page.css @@ -188,11 +188,11 @@ position: relative; } -#conversation-main .conversation-block.sender-in { +#conversation-main .conversation-block.sender-assistant { display: flex; } -#conversation-main .conversation-block.sender-in .avatar { +#conversation-main .conversation-block.sender-assistant .avatar { --avatar-size: 35px; width: var(--avatar-size); height: var(--avatar-size); @@ -205,7 +205,7 @@ font-size: 18px; } -#conversation-main .conversation-block.sender-out .message { +#conversation-main .conversation-block.sender-user .message { width: fit-content; padding: 13px 30px; border-radius: 13px; @@ -215,11 +215,11 @@ margin-left: auto; } -#conversation-main .conversation-block.sender-in .message { +#conversation-main .conversation-block.sender-assistant .message { line-height: 27px; } -#conversation-main .conversation-block.sender-in .model-name { +#conversation-main .conversation-block.sender-assistant .model-name { font-weight: bold; margin-bottom: 5px; } @@ -232,18 +232,18 @@ 100% { transform: translateY(0px); } } -#conversation-main .conversation-block.sender-in .message .dot-animation { +#conversation-main .conversation-block.sender-assistant .message .dot-animation { animation: dot-animation 1.2s linear infinite; width: 10px; margin: 5px; display: inline-block; } -#conversation-main .conversation-block.sender-in .message .dot-animation.dot-2 { +#conversation-main .conversation-block.sender-assistant .message .dot-animation.dot-2 { animation-delay: .3s; } -#conversation-main .conversation-block.sender-in .message .dot-animation.dot-3 { +#conversation-main .conversation-block.sender-assistant .message .dot-animation.dot-3 { animation-delay: .6s; } From 126a931ada799461e9c3f2a3944e36b1f4f26f2a Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:10:56 +1000 Subject: [PATCH 04/34] add functions to manage cookies Signed-off-by: cbh778899 --- tools/cookies.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tools/cookies.js diff --git a/tools/cookies.js b/tools/cookies.js new file mode 100644 index 0000000..aca4b65 --- /dev/null +++ b/tools/cookies.js @@ -0,0 +1,52 @@ +function writeObjToCookie(cookie_items) { + document.cookie = + Object.entries(cookie_items).map(([key, value])=>{ + return `${key}=${value}` + }).join('; '); +} + +function loadObjFromCookie() { + const cookie_items = {} + document.cookie && document.cookie.split('; ') + .forEach(cookie_section=>{ + const [key, value] = cookie_section.split('='); + cookie_items[key] = value; + }) + return cookie_items; +} + +export default function cookies() { + const cookie_items = loadObjFromCookie(); + + function getItem(key) { + return cookie_items[key]; + } + + function setItem(key, value) { + // if key is invalid + if(typeof key !== "string" || /(; |=)/.test(key)) { + return null; + } + // we only accept string + if(typeof value === 'undefined') { + return null; + } else if(typeof value === 'object') { + value = JSON.stringify(value); + } else { + value = `${value}`; + } + // set value + cookie_items[key] = value; + writeObjToCookie(cookie_items); + return value; + } + + function removeItem(key) { + const value = cookie_items[key]; + delete cookie_items[key]; + writeObjToCookie(cookie_items); + return value; + } + + return {getItem, setItem, removeItem} +} \ No newline at end of file From 68aef6ce82f86339f10b010232cc81bb3e495b34 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:11:54 +1000 Subject: [PATCH 05/34] use different session title Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index c98ec36..b32861a 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -129,7 +129,9 @@ async function sendMessage(message, send) { togglePending(); if(!conversation.history.length) { main_elem.innerHTML = '' - updateHistoryName(conversation.id, message.substring(0, 20)) + const message_len = message.length; + updateHistoryName(conversation.id, + `${message.substring(0, 25)}${message_len > 25 ? '...' : ''}`) } main_elem.appendChild(createBlock('user', message)[0]); main_elem.scrollTo({ From 2a1d1e0a151154d42854d2ef13f63f55c47e1506 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:12:13 +1000 Subject: [PATCH 06/34] implement user page Signed-off-by: cbh778899 --- components/account-page/index.js | 115 ++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index 0dd8b0d..f05753a 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -1,6 +1,16 @@ import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js"; +import useUser from '../../global/useUser.js' -let account_dialog; +let account_dialog = null, input_details_main = null; +let current_user = {}, isRegister = false; + +const { + register, login, logout, +} = useUser(user=>{ + if(!input_details_main) return; + current_user = user; + createInputDetailsPage(); +}); function toggleDialog(force = null) { if(!account_dialog) return false; @@ -29,6 +39,77 @@ const account_fields = { ] } +function createInputDetailsPage() { + input_details_main.innerHTML = ''; + // input fields + account_fields[ + current_user.logged_in ? 'logged_in' : + isRegister ? 'register' : 'login' + ] + .forEach(e=>{ + input_details_main.appendChild( + createAccountInputFields(e) + ) + }) + // hr + input_details_main.insertAdjacentHTML("beforeend", "
") + // buttons + const submit_btn = document.createElement('button'); + submit_btn.type = 'submit'; + submit_btn.className = 'function-btn clickable'; + input_details_main.appendChild(submit_btn); + + const functional_btn = document.createElement('button'); + functional_btn.type = 'button'; + functional_btn.className = 'function-btn reverse-color clickable'; + input_details_main.appendChild(functional_btn); + + function updateBtnText() { + submit_btn.textContent = + current_user.logged_in ? 'Update' : + isRegister ? 'Register Now' : 'Login Now'; + + functional_btn.textContent = + current_user.logged_in ? 'Logout' : + isRegister ? 'I want to Login!' : 'I want to Register!'; + } + updateBtnText(); + + functional_btn.onclick = evt => { + evt.preventDefault(); + if(current_user.logged_in) { + logout(); + } else { + updateBtnText(); + isRegister = !isRegister; + createInputDetailsPage(); + } + } + +} + +function submitDetails(evt) { + evt.preventDefault(); + + if(current_user.logged_in) {return;} + else if(isRegister) { + const username = evt.target.username.value; + const email = evt.target.email.value; + const password = evt.target.password.value; + const repeat_password = evt.target['repeat-password'].value; + + if(password !== repeat_password) { + evt.target['repeat-password'].classList.add('error'); + return; + } + register(username, email, password); + } else { + const username = evt.target.username.value; + const password = evt.target.password.value; + login(username, password); + } +} + export default function createAccountPage() { if(toggleDialog()) return; @@ -39,35 +120,18 @@ export default function createAccountPage() { const account_main_page = document.createElement('form'); account_main_page.className = 'account-main'; account_main_page.onclick = evt => evt.stopPropagation(); + account_main_page.onsubmit = submitDetails; account_main_page.insertAdjacentHTML("afterbegin", `
`) - const input_main = document.createElement('div'); - input_main.className = 'input-details-main'; - - // TODO: if logged in - account_fields.login.forEach(e=>{ - input_main.appendChild(createAccountInputFields(e)) - }) - - input_main.insertAdjacentHTML("beforeend", "
") - - const submit_button = document.createElement('button') - submit_button.type = 'submit' - submit_button.className = 'submit-button clickable' - submit_button.textContent = 'Login' - input_main.appendChild(submit_button) - - const switch_btn = document.createElement('button') - switch_btn.type = 'submit' - switch_btn.className = 'submit-button reverse-color clickable' - switch_btn.textContent = 'I want to register' - input_main.appendChild(switch_btn) - - account_main_page.appendChild(input_main) - account_dialog.appendChild(account_main_page) + input_details_main = document.createElement('div'); + input_details_main.className = 'input-details-main'; + + account_main_page.appendChild(input_details_main); + account_dialog.appendChild(account_main_page); + createInputDetailsPage(); toggleDialog(); } @@ -81,6 +145,7 @@ function createAccountInputFields({index, title = null, type = null}) { const input = document.createElement('input'); input.type = type || 'text'; + input.name = index; field_container.appendChild(title_element); field_container.appendChild(input); From 3997d584e5b184e7623b50a6aa13eb8863181af7 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:12:49 +1000 Subject: [PATCH 07/34] add custom hook to manage user information Signed-off-by: cbh778899 --- global/useUser.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 global/useUser.js diff --git a/global/useUser.js b/global/useUser.js new file mode 100644 index 0000000..a52e908 --- /dev/null +++ b/global/useUser.js @@ -0,0 +1,76 @@ +import cookies from "../tools/cookies.js"; +import request from "../tools/request.js"; +import createHook from "./createHook.js"; + +let userInfo = { + logged_in: false, + id: null, + token: "", + username: "", + email: "" +} + +const cookie = cookies(); +const current_user_info = cookie.getItem('current-user'); +if(current_user_info) { + // current we have user logged in but somehow we lost it, e.g. user refreshed page + const info = JSON.parse(current_user_info); + userInfo = { + ...info, logged_in: true + } +} + +const { onmount, remount, dismount, updateAll } = createHook(); + +export default function useUser(updated) { + const mount_key = onmount(updated) + + async function login(username, password) { + const {id, authorizedAccount} = await request('/auth/signin', { + method: 'POST', + body: { username, password } + }); + + const { token, email } = authorizedAccount; + userInfo = { + ...userInfo, username, email, + id, token, logged_in: true + } + cookie.setItem('current-user', { id, username, email, token }) + updateAll(userInfo); + } + + async function register(username, email, password) { + const {id, authorizedAccount} = await (await request('/auth/signup', { + method: 'POST', + body: { username, email, password } + })).json() + + const { token } = authorizedAccount; + userInfo = { + ...userInfo, username,email, + id, token, logged_in: true + } + cookie.setItem('current-user', { id, username, email, token }) + updateAll(userInfo); + } + + function logout() { + userInfo = { + logged_in: false, + id: null, + token: "", + username: "", + email: "" + } + cookie.removeItem('current-user'); + updateAll(userInfo); + } + + updated && updated(userInfo); + + return { + login, register, logout, + componetDismount: dismount(mount_key), componentReMount:remount(mount_key) + } +} \ No newline at end of file From 2af9a456d87a84171ecdce4cde570aa78f882a61 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:13:44 +1000 Subject: [PATCH 08/34] update style so it won't looks strange Signed-off-by: cbh778899 --- styles/account_page.css | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/styles/account_page.css b/styles/account_page.css index 6f3d6ae..12f059e 100644 --- a/styles/account_page.css +++ b/styles/account_page.css @@ -32,12 +32,16 @@ --item-height: 40px; position: absolute; - width: var(--details-width); - height: 100%; + width: calc(var(--details-width) - 20px); + height: calc(100% - 40px); top: 0; right: 0; - padding: 30px; align-content: center; + overflow-y: auto; + overflow-x: hidden; + margin: 20px; + margin-right: unset; + padding-right: 20px; } .input-details-main .account-field-container { @@ -45,12 +49,15 @@ height: fit-content; position: relative; margin-bottom: 10px; + max-width: 100%; } .input-details-main .account-field-container .title { font-size: 18px; margin-bottom: 5px; font-weight: bold; + position: relative; + max-width: 100%; } .input-details-main .account-field-container input { @@ -61,10 +68,15 @@ box-sizing: border-box; padding: 0px 15px; font-size: 16px; + position: relative; + max-width: 100%; } .input-details-main .account-field-container input:focus { outline: none; } +.input-details-main .account-field-container input.error { + border-color: red; +} .input-details-main hr { width: var(--item-width); @@ -72,9 +84,10 @@ margin: auto; margin-left: 0; margin-top: 20px; + max-width: 100%; } -.input-details-main .submit-button { +.input-details-main .function-btn { width: var(--item-width); height: calc(var(--item-height) + 5px); border-radius: 16px; @@ -86,9 +99,11 @@ font-weight: bold; margin-top: 20px; display: block; + position: relative; + max-width: 100%; } -.input-details-main .submit-button.reverse-color { +.input-details-main .function-btn.reverse-color { background-color: white; border: 1px solid dodgerblue; color: dodgerblue; From d3a088d9ac37153f50558bef311667e0e0569d4c Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:27:30 +1000 Subject: [PATCH 09/34] update request function to directly return json on demand Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 2 +- components/info-page.js | 3 +-- global/useConversation.js | 2 +- global/useHistory.js | 2 +- global/useSessionId.js | 2 +- global/useUser.js | 4 ++-- tools/request.js | 17 +++++++++++++++-- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index b32861a..de0c1e4 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -147,7 +147,7 @@ async function sendMessage(message, send) { sessionUuid: conversation.id || "uuid", message, ...model_settings } - }) + }, true) const content = await send(response, updateMessage); togglePending(); diff --git a/components/info-page.js b/components/info-page.js index eab3064..0faf737 100644 --- a/components/info-page.js +++ b/components/info-page.js @@ -16,8 +16,7 @@ export default function createInfoPage() { async function updateVersions() { - const query_versions = await (await request('version')).json(); - + const query_versions = await request('version'); const versions = { rebel: `v${VERSION}`, ...query_versions diff --git a/global/useConversation.js b/global/useConversation.js index effea87..1ab08aa 100644 --- a/global/useConversation.js +++ b/global/useConversation.js @@ -23,7 +23,7 @@ export default function useConversation(updated) { async function startNewConversation() { storeHistory(); - const { sessionUuid } = await (await request('chat/seesionuuid')).json(); + const { sessionUuid } = await request('chat/seesionuuid'); currentConversation = { id: sessionUuid, history: [] }; diff --git a/global/useHistory.js b/global/useHistory.js index 6b896e7..7254870 100644 --- a/global/useHistory.js +++ b/global/useHistory.js @@ -25,7 +25,7 @@ export default function useHistory(updated = null) { async function requestUpdateHistory() { if(!currentSession) return; - const chat_history = await (await request('chat')).json(); + const chat_history = await request('chat'); history.length = 0; chat_history.forEach(({sessionUuid, name, createdAt}) => { diff --git a/global/useSessionId.js b/global/useSessionId.js index 029cd8a..8f12638 100644 --- a/global/useSessionId.js +++ b/global/useSessionId.js @@ -9,7 +9,7 @@ export default function useSessionId(updated) { const mount_key = onmount(updated) async function genSession() { - const { token } = await (await request('auth/token')).json() + const { token } = await request('auth/token'); currentSession = token; updateAll(currentSession); } diff --git a/global/useUser.js b/global/useUser.js index a52e908..ea4abcd 100644 --- a/global/useUser.js +++ b/global/useUser.js @@ -41,10 +41,10 @@ export default function useUser(updated) { } async function register(username, email, password) { - const {id, authorizedAccount} = await (await request('/auth/signup', { + const {id, authorizedAccount} = await request('/auth/signup', { method: 'POST', body: { username, email, password } - })).json() + }) const { token } = authorizedAccount; userInfo = { diff --git a/tools/request.js b/tools/request.js index 2b60045..1dbc9e5 100644 --- a/tools/request.js +++ b/tools/request.js @@ -4,8 +4,21 @@ import useSessionId from "../global/useSessionId.js"; let session_id = ''; useSessionId(id=>{session_id = id}); -export default function request(url, options={}) { - return fetch(reqAddress(url), generateRequest(url, options)) +export default async function request(url, options={}, not_return_json = false) { + let res = null; + try { + const address = reqAddress(url); + const request_options = generateRequest(url, options); + if(not_return_json) { + return fetch(address, request_options); + } else { + res = await fetch(address, request_options); + return await res.json(); + } + } catch (error) { + console.error(error); + return res || {} + } } export function reqAddress(url) { From b08a9247c46c66f6e91c4a1d349c2001d8a1a14c Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:48:59 +1000 Subject: [PATCH 10/34] update isRegister to false after login Signed-off-by: cbh778899 --- components/account-page/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index f05753a..dda4d30 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -102,7 +102,7 @@ function submitDetails(evt) { evt.target['repeat-password'].classList.add('error'); return; } - register(username, email, password); + register(username, email, password).then(()=>isRegister=false); } else { const username = evt.target.username.value; const password = evt.target.password.value; From c956ea62faf748096bc19b5f19448d831fb75916 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 17:51:16 +1000 Subject: [PATCH 11/34] rename type to role Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index de0c1e4..1e2a4d9 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -153,8 +153,8 @@ async function sendMessage(message, send) { togglePending(); appendConversationMessage([ - { type: 'user', message }, - { type: 'assistant', message: content} + { role: 'user', message }, + { role: 'assistant', message: content} ], conversation.id) } @@ -200,8 +200,8 @@ function updateConversation() { } main_elem.innerHTML = '' - conversation.history.forEach(({type, message})=>{ - main_elem.appendChild(createBlock(type, message)[0]) + conversation.history.forEach(({role, message})=>{ + main_elem.appendChild(createBlock(role, message)[0]) }) main_elem.scrollTo({ top: main_elem.scrollHeight, @@ -209,16 +209,16 @@ function updateConversation() { }) } -function createBlock(type, msg = '') { +function createBlock(role, msg = '') { const block = document.createElement('div'); - block.className = `conversation-block sender-${type}`; + block.className = `conversation-block sender-${role}`; const message = document.createElement('div'); message.className = 'message'; block.appendChild(message); - if(type === 'assistant') { + if(role === 'assistant') { message.innerHTML = ` ${getSVG('circle-fill', 'dot-animation dot-1')} ${getSVG('circle-fill', 'dot-animation dot-2')} From 3188608647d4a74361ecb9a6d19405229af333e0 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 18:22:24 +1000 Subject: [PATCH 12/34] can normally save/load history now Signed-off-by: cbh778899 --- global/useConversation.js | 30 ++++++++++++++++++++---------- global/useUser.js | 35 +++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/global/useConversation.js b/global/useConversation.js index 1ab08aa..14e7806 100644 --- a/global/useConversation.js +++ b/global/useConversation.js @@ -1,6 +1,7 @@ import request from "../tools/request.js"; import createHook from "./createHook.js"; import useHistory from "./useHistory.js"; +import useUser from "./useUser.js"; let currentConversation = { id: null, @@ -10,8 +11,11 @@ let currentConversation = { const conversation_histories = {} +let currentUser; + const { onmount, remount, dismount, updateAll } = createHook(); const { addHistory } = useHistory(null); +useUser(user=>currentUser = user); export default function useConversation(updated) { const mount_key = onmount(updated); @@ -40,21 +44,27 @@ export default function useConversation(updated) { updateAll(currentConversation); } - function sendMessage(messages) { + async function sendMessage(messages) { + await request('chat/save', { + method: 'POST', + body: { + sessionUuid: currentConversation.id, + chats: messages + } + }) currentConversation.history.push(...messages); updateAll(currentConversation); } async function selectConversation(id) { - // TODO: logged in user should query from backend - - // const conversation_history = []; - // await (await request(`chat/history/${id}`)) - // .json().forEach(({type, message})=>{ - // conversation_history.push({type, message}); - // }) - storeHistory(); - currentConversation = { id, history: conversation_histories[id] } + let history; + if(currentUser.logged_in) { + history = await request(`chat/history/${id}`); + } else { + storeHistory(); + history = conversation_histories[id]; + } + currentConversation = { id, history }; updateAll(currentConversation); } diff --git a/global/useUser.js b/global/useUser.js index ea4abcd..5f92014 100644 --- a/global/useUser.js +++ b/global/useUser.js @@ -1,11 +1,12 @@ import cookies from "../tools/cookies.js"; import request from "../tools/request.js"; import createHook from "./createHook.js"; +import useHistory from "./useHistory.js"; +import useSessionId from "./useSessionId.js"; let userInfo = { logged_in: false, id: null, - token: "", username: "", email: "" } @@ -20,11 +21,25 @@ if(current_user_info) { } } +const { requestUpdateHistory } = useHistory(); +const { manualUpdateSession, genSession } = useSessionId(); + const { onmount, remount, dismount, updateAll } = createHook(); export default function useUser(updated) { const mount_key = onmount(updated) + function setLoggedIn(id, username, email, token) { + userInfo = { + ...userInfo, username, email, + id, logged_in: true + } + manualUpdateSession(token); + cookie.setItem('current-user', { id, username, email, token }) + updateAll(userInfo); + requestUpdateHistory(); + } + async function login(username, password) { const {id, authorizedAccount} = await request('/auth/signin', { method: 'POST', @@ -32,12 +47,7 @@ export default function useUser(updated) { }); const { token, email } = authorizedAccount; - userInfo = { - ...userInfo, username, email, - id, token, logged_in: true - } - cookie.setItem('current-user', { id, username, email, token }) - updateAll(userInfo); + setLoggedIn(id, username, email, token); } async function register(username, email, password) { @@ -47,15 +57,10 @@ export default function useUser(updated) { }) const { token } = authorizedAccount; - userInfo = { - ...userInfo, username,email, - id, token, logged_in: true - } - cookie.setItem('current-user', { id, username, email, token }) - updateAll(userInfo); + setLoggedIn(id, username, email, token); } - function logout() { + async function logout() { userInfo = { logged_in: false, id: null, @@ -65,6 +70,8 @@ export default function useUser(updated) { } cookie.removeItem('current-user'); updateAll(userInfo); + await genSession(); + requestUpdateHistory(); } updated && updated(userInfo); From d12f8d3cb598a9f4e9137da3bfb617a48b79b2ac Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 21:34:04 +1000 Subject: [PATCH 13/34] fix url Signed-off-by: cbh778899 --- global/useUser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/global/useUser.js b/global/useUser.js index 5f92014..652e001 100644 --- a/global/useUser.js +++ b/global/useUser.js @@ -41,7 +41,7 @@ export default function useUser(updated) { } async function login(username, password) { - const {id, authorizedAccount} = await request('/auth/signin', { + const {id, authorizedAccount} = await request('auth/signin', { method: 'POST', body: { username, password } }); @@ -51,7 +51,7 @@ export default function useUser(updated) { } async function register(username, email, password) { - const {id, authorizedAccount} = await request('/auth/signup', { + const {id, authorizedAccount} = await request('auth/signup', { method: 'POST', body: { username, email, password } }) From bf6dc17ca76a6e1352b56d85f10d1d00b57b4a8b Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Fri, 12 Jul 2024 21:34:47 +1000 Subject: [PATCH 14/34] fix history & conversation page switch when login/logout Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 36 +++++++++++++++++++++----------- global/useConversation.js | 11 +++++++++- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index 1e2a4d9..231f764 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -7,7 +7,7 @@ import getSVG from "../../tools/svgs.js"; let conversation = {}, model_settings = {}, main_elem, toggle_expand, - stream_response=true; + stream_response=true, init = false; const { componetDismount: conversationDismount, @@ -20,7 +20,16 @@ const { submit_icon && submit_icon.classList.toggle('pending', conversation.pending) if(c.id === conversation.id) return; conversation = c; - if(!conversation.id) return; + if(!conversation.id) { + if(main_elem) { + main_elem.innerHTML = ` +
+ Please select a ticket or start a new conversation on left. +
` + document.getElementById('submit-chat').innerHTML = ''; + } + return; + } updateConversation(); buildForm(); @@ -66,19 +75,22 @@ export default function createChatMain(main, toggleExpand, openModelSetting) { `) - if(!toggle_expand) toggle_expand = toggleExpand; - - document.getElementById('submit-chat').onsubmit=submitContent; - main_elem = document.getElementById('conversation-main'); - document.getElementById('toggle-sidebar-expand').onclick = toggle_expand; - document.getElementById('toggle-setting-page').onclick = openModelSetting; - document.getElementById('download-conversation').onclick = () => { - if(conversation.id && conversation.history.length) { - formatJSON(conversation, getHistory(conversation.id)) + if(!init) { + toggle_expand = toggleExpand; + document.getElementById('submit-chat').onsubmit=submitContent; + main_elem = document.getElementById('conversation-main'); + document.getElementById('toggle-sidebar-expand').onclick = toggle_expand; + document.getElementById('toggle-setting-page').onclick = openModelSetting; + document.getElementById('download-conversation').onclick = () => { + if(conversation.id && conversation.history.length) { + formatJSON(conversation, getHistory(conversation.id)) + } } + init = true; + } else { + modelSettingsRemount(); } - modelSettingsRemount(); if(conversationReMount() && conversation.id) { updateConversation(); buildForm(); diff --git a/global/useConversation.js b/global/useConversation.js index 14e7806..2288177 100644 --- a/global/useConversation.js +++ b/global/useConversation.js @@ -14,7 +14,16 @@ const conversation_histories = {} let currentUser; const { onmount, remount, dismount, updateAll } = createHook(); -const { addHistory } = useHistory(null); +const { addHistory } = useHistory(h=>{ + if(currentConversation.id) { + if(!h.filter(e=>e.id === currentConversation.id).length) { + currentConversation = { + id: null, pending: false, history: [] + }; + updateAll(currentConversation); + } + } +}); useUser(user=>currentUser = user); export default function useConversation(updated) { From f1659867acf2f250d825465f80df591c81b63e9e Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 15:06:30 +1000 Subject: [PATCH 15/34] add header Content-Type: application/json to patch request as well Signed-off-by: cbh778899 --- tools/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/request.js b/tools/request.js index 1dbc9e5..2ed4e7e 100644 --- a/tools/request.js +++ b/tools/request.js @@ -29,7 +29,7 @@ function generateRequest(url, options={}) { const headers = { Accept: 'application/json' } - if(options.method && options.method === 'POST') { + if(options.method && /^(POST|PATCH)$/i.test(options.method)) { headers['Content-Type'] = 'application/json' } if(/^(chat|accounts).*$/.test(url) && session_id) { From 89d2d50028587017837731032e64735461cad066 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 15:06:45 +1000 Subject: [PATCH 16/34] implement updateuserinfo Signed-off-by: cbh778899 --- components/account-page/index.js | 47 +++++++++++++++++++++++++++----- global/useUser.js | 16 ++++++++++- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index dda4d30..bf174a2 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -5,7 +5,7 @@ let account_dialog = null, input_details_main = null; let current_user = {}, isRegister = false; const { - register, login, logout, + register, login, logout, updateUserInfo } = useUser(user=>{ if(!input_details_main) return; current_user = user; @@ -30,12 +30,13 @@ const account_fields = { { index: 'username' }, { index: 'email' }, { index: 'password', type: 'password' }, - { index: 'repeat-password', title: 'Repeat Password', type: 'password' } + { index: 'repeat-password', title: 'Confirm Password', type: 'password' } ], logged_in: [ - { index: 'username', title: 'Update Username' }, + { index: 'username', readonly: true }, { index: 'email', title: 'Update Email' }, - { index: 'password', title: 'Update Password', type: 'password' }, + { index: 'new-password', title: 'New Password', type: 'password' }, + { index: 'repeat-new-password', title: 'Confirm New Password', type: 'password' }, ] } @@ -47,6 +48,17 @@ function createInputDetailsPage() { isRegister ? 'register' : 'login' ] .forEach(e=>{ + if(current_user.logged_in) { + if(e.index === 'username') e.value = current_user.username; + else if(e.index === 'email') e.value = current_user.email; + } else if(!isRegister) { + const saved_user_login_info = localStorage.getItem('saved-user-login-info'); + if(saved_user_login_info) { + const info = JSON.parse(saved_user_login_info); + if(e.index === 'username') e.value = info.username; + else if(e.index === 'email') e.value = info.password; + } + } input_details_main.appendChild( createAccountInputFields(e) ) @@ -91,8 +103,24 @@ function createInputDetailsPage() { function submitDetails(evt) { evt.preventDefault(); - if(current_user.logged_in) {return;} - else if(isRegister) { + if(current_user.logged_in) { + const email = evt.target.email.value; + const new_password = evt.target['new-password'].value; + const repeat_new_password = evt.target['repeat-new-password'].value; + const submit_values = {} + if(email) submit_values.email = email; + if(new_password) { + if(repeat_new_password !== new_password) { + evt.target['repeat-new-password'].classList.add('error'); + return; + } + submit_values.password = new_password; + } + updateUserInfo(submit_values).then(res=>{ + // TODO: show updated or not + console.log(res) + }); + } else if(isRegister) { const username = evt.target.username.value; const email = evt.target.email.value; const password = evt.target.password.value; @@ -135,7 +163,10 @@ export default function createAccountPage() { toggleDialog(); } -function createAccountInputFields({index, title = null, type = null}) { +function createAccountInputFields({ + index, title = '', type = '', + readonly = false, value = '' +}) { const field_container = document.createElement('div'); field_container.className = 'account-field-container'; @@ -146,6 +177,8 @@ function createAccountInputFields({index, title = null, type = null}) { const input = document.createElement('input'); input.type = type || 'text'; input.name = index; + input.value = value; + if(readonly) input.readOnly = 'true'; field_container.appendChild(title_element); field_container.appendChild(input); diff --git a/global/useUser.js b/global/useUser.js index 652e001..0009c3e 100644 --- a/global/useUser.js +++ b/global/useUser.js @@ -74,10 +74,24 @@ export default function useUser(updated) { requestUpdateHistory(); } + async function updateUserInfo(fields) { + const {id, authorizedAccount, detail} = await request('accounts', { + method: 'PATCH', + body: fields + }) + if(!detail) { + userInfo = { + ...userInfo, + id, email: authorizedAccount.email + } + return true; + } return false; + } + updated && updated(userInfo); return { - login, register, logout, + login, register, logout, updateUserInfo, componetDismount: dismount(mount_key), componentReMount:remount(mount_key) } } \ No newline at end of file From 8387b01d8f25804dee80d892d1e834a2d04e3149 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 16:05:39 +1000 Subject: [PATCH 17/34] update login page style Signed-off-by: cbh778899 --- components/account-page/index.js | 31 +++++++++++++-- styles/account_page.css | 66 ++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index bf174a2..ff5043e 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -1,5 +1,6 @@ -import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js"; import useUser from '../../global/useUser.js' +import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js"; +import getSVG from "../../tools/svgs.js"; let account_dialog = null, input_details_main = null; let current_user = {}, isRegister = false; @@ -108,7 +109,9 @@ function submitDetails(evt) { const new_password = evt.target['new-password'].value; const repeat_new_password = evt.target['repeat-new-password'].value; const submit_values = {} - if(email) submit_values.email = email; + if(email && email !== current_user.email) { + submit_values.email = email; + } if(new_password) { if(repeat_new_password !== new_password) { evt.target['repeat-new-password'].classList.add('error'); @@ -178,13 +181,35 @@ function createAccountInputFields({ input.type = type || 'text'; input.name = index; input.value = value; + input.placeholder = ' ' + input.autocomplete = 'off' if(readonly) input.readOnly = 'true'; + field_container.onclick = () => input.focus(); field_container.appendChild(title_element); field_container.appendChild(input); if(type === 'password') { - // TODO + let show_password = false; + + const eye_icon = document.createElement('div'); + eye_icon.className = 'password-eye-icon clickable'; + + const toggleShowPassword = () => { + if(!show_password) { + input.type = 'password'; + eye_icon.innerHTML = getSVG('eye'); + eye_icon.title = 'Show Password'; + } else { + input.type = 'text'; + eye_icon.innerHTML = getSVG('eye-slash'); + eye_icon.title = 'Hide Password'; + } + show_password = !show_password; + } + toggleShowPassword(); + eye_icon.onclick = toggleShowPassword; + field_container.appendChild(eye_icon); } return field_container; diff --git a/styles/account_page.css b/styles/account_page.css index 12f059e..7f02a72 100644 --- a/styles/account_page.css +++ b/styles/account_page.css @@ -29,7 +29,7 @@ .account-main .input-details-main { --item-width: 230px; - --item-height: 40px; + --item-height: 45px; position: absolute; width: calc(var(--details-width) - 20px); @@ -45,38 +45,70 @@ } .input-details-main .account-field-container { - width: fit-content; - height: fit-content; + width: var(--item-width); + height: var(--item-height); position: relative; - margin-bottom: 10px; + margin-bottom: 15px; max-width: 100%; + border: 1px solid gray; + border-radius: 5px; +} +.input-details-main .account-field-container:hover { + cursor: text; +} + +.input-details-main .account-field-container:has(input:focus) { + border-color: dodgerblue; } .input-details-main .account-field-container .title { + position: absolute; + color: gray; font-size: 18px; - margin-bottom: 5px; - font-weight: bold; - position: relative; - max-width: 100%; + width: 100%; + height: 100%; + line-height: var(--item-height); + left: 10px; + top: 0px; + transition-duration: .5s; + transform-origin: top left; + user-select: none; +} + +.input-details-main .account-field-container:has(input:focus) .title, +.input-details-main .account-field-container:has(input:not(:placeholder-shown)) .title + { + transform: scale(0.6); +} +.input-details-main .account-field-container:has(input:focus) .title { + color: dodgerblue; } .input-details-main .account-field-container input { - width: var(--item-width); - height: var(--item-height); - border: 1px solid gray; - border-radius: 5px; + width: 100%; + height: 30%; + border: none; box-sizing: border-box; padding: 0px 15px; font-size: 16px; - position: relative; + position: absolute; max-width: 100%; + bottom: 5px; + background-color: transparent; } .input-details-main .account-field-container input:focus { outline: none; } -.input-details-main .account-field-container input.error { - border-color: red; + +.password-eye-icon { + position: absolute; + height: var(--item-height); + width: var(--item-height); + display: flex; + align-items: center; + right: 0; } +.password-eye-icon svg { margin: auto; } .input-details-main hr { width: var(--item-width); @@ -89,7 +121,7 @@ .input-details-main .function-btn { width: var(--item-width); - height: calc(var(--item-height) + 5px); + height: calc(var(--item-height) - 3px); border-radius: 16px; background-color: dodgerblue; color: white; @@ -97,7 +129,7 @@ border: none; font-size: 18px; font-weight: bold; - margin-top: 20px; + margin-top: 17px; display: block; position: relative; max-width: 100%; From 23f8e38fe74b7184e81cfa434628b822bff7f370 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:05:22 +1000 Subject: [PATCH 18/34] implement show message Signed-off-by: cbh778899 --- index.html | 1 + styles/message.css | 59 +++++++++++++++++++++++++++++++++++++++++++ tools/message.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++ tools/svgs.js | 19 ++++++++++++-- 4 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 styles/message.css create mode 100644 tools/message.js diff --git a/index.html b/index.html index c161484..4c2119e 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@ + diff --git a/styles/message.css b/styles/message.css new file mode 100644 index 0000000..e656836 --- /dev/null +++ b/styles/message.css @@ -0,0 +1,59 @@ +.message-dialog { + pointer-events: none; + z-index: 100; +} + +.message-dialog::backdrop { + background-color: transparent; +} + +.message-dialog * { + pointer-events: initial; +} + + +@keyframes slide-in { + from { transform: translateX(100%); } + to { transform: translateX(0); } +} + +@keyframes slide-out { + from { transform: translateX(0); } + to { transform: translateX(100%); } +} + +.message-dialog .message { + position: relative; + margin-right: 5px; + margin-left: auto; + width: fit-content; + height: fit-content; + min-width: 300px; + min-height: 50px; + padding: 10px 15px; + background-color: white; + border-radius: 10px; + display: flex; + animation: slide-in forwards; + margin-bottom: 5px; + background-color: white; + border: 1px solid; + color: dodgerblue; +} + +.message.err,.message.error { color: red; } +.message.warn,.message.warning { color: goldenrod; } +.message.success { color: limegreen; } + +.message-dialog .message .message-icon { + width: 30px; + height: 30px; + margin-right: 20px; + color: inherit; +} + +.message-dialog .message .message-text { + font-size: 17px; + padding: 5px 0px; + color: black; +} \ No newline at end of file diff --git a/tools/message.js b/tools/message.js new file mode 100644 index 0000000..41ca40b --- /dev/null +++ b/tools/message.js @@ -0,0 +1,63 @@ +import getSVG from "./svgs.js"; + +const message_dialog = document.createElement('dialog'); +message_dialog.className = 'message-dialog'; +document.body.appendChild(message_dialog); +message_dialog.showModal(); + +/** + * @typedef messageOptions + * @property { 'info'|'information'|'err'|'error'|'warn'|'warning'|'success' } type - Type of message, default to 'info' + * @property { Number } timeout - Milliseconds of time before information block disappear, default to 5000 + * @property { Number } animation_duration - Milliseconds of time information block move in and out, default to 300 + * @property { Boolean } close_on_click - Set whether information block should close when clicked + */ + +/** + * Display a notification with given type on top-right corner + * @param {String} message - The message to show, can be html element in string + * @param {messageOptions} options - Options of showing messages + */ +export default function showMessage(message, options = {}) { + const type = options.type || 'info'; + const timeout = options.timeout || 5000; + const animation_duration = options.animation_duration || 300; + const close_on_click = options.close_on_click || true; + + let icon; + switch(type) { + case 'err': case 'error': + icon = 'x-circle-fill'; break; + case 'warn': case 'warning': + icon = 'exclamation-circle-fill'; break; + case 'success': + icon = 'check-circle-fill'; break; + case 'info': case 'information': + default: icon = 'info-circle-fill'; break; + } + + const message_elem = document.createElement('div'); + message_elem.className = `message ${type} ${close_on_click ? ' clickable':''}`; + message_elem.innerHTML = ` + ${getSVG(icon, 'message-icon')} +
${message}
+ ` + message_elem.style.animationDuration = `${animation_duration}ms` + message_dialog.appendChild(message_elem); + + const removeMessage = () => { + message_elem.style.animationName = 'slide-out'; + setTimeout(() => { + message_elem.remove(); + }, animation_duration); + } + + const timer = setTimeout(removeMessage, timeout + animation_duration); + + if(close_on_click) { + message_elem.onclick = () => { + clearTimeout(timer); + removeMessage(); + } + } +} \ No newline at end of file diff --git a/tools/svgs.js b/tools/svgs.js index c57e3ae..a775442 100644 --- a/tools/svgs.js +++ b/tools/svgs.js @@ -109,16 +109,31 @@ const svgs = { `, "eye": ` - + `, "eye-slash": ` - + + `, + + "exclamation-circle-fill": ` + + + `, + + "check-circle-fill": ` + + + `, + + "x-circle-fill": ` + + ` } From 1432c9d9bb4f725be4be5501cbbae498684faf41 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:05:45 +1000 Subject: [PATCH 19/34] handle request HTTP error Signed-off-by: cbh778899 --- tools/request.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tools/request.js b/tools/request.js index 2ed4e7e..0bd2f22 100644 --- a/tools/request.js +++ b/tools/request.js @@ -5,19 +5,18 @@ let session_id = ''; useSessionId(id=>{session_id = id}); export default async function request(url, options={}, not_return_json = false) { - let res = null; - try { - const address = reqAddress(url); - const request_options = generateRequest(url, options); - if(not_return_json) { - return fetch(address, request_options); + // let res = null; + const address = reqAddress(url); + const request_options = generateRequest(url, options); + if(not_return_json) { + return fetch(address, request_options); + } else { + const res = await fetch(address, request_options); + if(res.ok) { + return res.json() } else { - res = await fetch(address, request_options); - return await res.json(); + return { http_error: true } } - } catch (error) { - console.error(error); - return res || {} } } From 6ab47c7e4e1c6d1e5da8912be54952fc1c7790e3 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:06:07 +1000 Subject: [PATCH 20/34] return to check if need to show message Signed-off-by: cbh778899 --- global/useUser.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/global/useUser.js b/global/useUser.js index 0009c3e..f7368a4 100644 --- a/global/useUser.js +++ b/global/useUser.js @@ -41,23 +41,29 @@ export default function useUser(updated) { } async function login(username, password) { - const {id, authorizedAccount} = await request('auth/signin', { + const {id, authorizedAccount, http_error} = await request('auth/signin', { method: 'POST', body: { username, password } }); + if(http_error) return false; + const { token, email } = authorizedAccount; setLoggedIn(id, username, email, token); + return true; } async function register(username, email, password) { - const {id, authorizedAccount} = await request('auth/signup', { + const {id, authorizedAccount, http_error} = await request('auth/signup', { method: 'POST', body: { username, email, password } }) + if(http_error) return false; + const { token } = authorizedAccount; setLoggedIn(id, username, email, token); + return true; } async function logout() { @@ -75,11 +81,11 @@ export default function useUser(updated) { } async function updateUserInfo(fields) { - const {id, authorizedAccount, detail} = await request('accounts', { + const {id, authorizedAccount, http_error} = await request('accounts', { method: 'PATCH', body: fields }) - if(!detail) { + if(!http_error) { userInfo = { ...userInfo, id, email: authorizedAccount.email From c85f594dbc970c1d7a21544b94e4fb40c17f7602 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:29:49 +1000 Subject: [PATCH 21/34] update style Signed-off-by: cbh778899 --- styles/account_page.css | 12 ++++++++++++ styles/index.css | 2 ++ styles/message.css | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/styles/account_page.css b/styles/account_page.css index 7f02a72..3ac69f4 100644 --- a/styles/account_page.css +++ b/styles/account_page.css @@ -1,3 +1,15 @@ +#user-popup-container { + content: ''; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.1); + position: fixed; + left: 0; + top: 0; + z-index: 10; + display: none; +} + .account-main { --img-width: 60%; --details-width: calc(100% - var(--img-width)); diff --git a/styles/index.css b/styles/index.css index 3769006..d808ac8 100644 --- a/styles/index.css +++ b/styles/index.css @@ -42,7 +42,9 @@ dialog { padding: 0; margin: 0; background-color: transparent; + outline: none; } +dialog:focus-visible { outline: none !important; } .clickable:hover { cursor: pointer; diff --git a/styles/message.css b/styles/message.css index e656836..100e7b9 100644 --- a/styles/message.css +++ b/styles/message.css @@ -35,7 +35,7 @@ border-radius: 10px; display: flex; animation: slide-in forwards; - margin-bottom: 5px; + margin-top: 5px; background-color: white; border: 1px solid; color: dodgerblue; From d8977fb8b9c69eeb54d73cd3c3402a7ab8490d6a Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:30:19 +1000 Subject: [PATCH 22/34] implement change user info Signed-off-by: cbh778899 --- components/account-page/index.js | 50 ++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index ff5043e..5ac20af 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -1,8 +1,9 @@ import useUser from '../../global/useUser.js' import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js"; import getSVG from "../../tools/svgs.js"; +import showMessage from "../../tools/message.js"; -let account_dialog = null, input_details_main = null; +let account_container = null, input_details_main = null, init = false, open_status = false; let current_user = {}, isRegister = false; const { @@ -14,10 +15,14 @@ const { }); function toggleDialog(force = null) { - if(!account_dialog) return false; + if(!init) return false; - let open_status = force === null ? !account_dialog.open : force; - open_status ? account_dialog.showModal() : account_dialog.close(); + open_status = force === null ? !open_status : force; + if(open_status) { + account_container.style.display = 'block'; + } else { + account_container.style.display = 'none'; + } return true; } @@ -91,7 +96,7 @@ function createInputDetailsPage() { functional_btn.onclick = evt => { evt.preventDefault(); if(current_user.logged_in) { - logout(); + logout().then(()=>showMessage('User Logged Out.')); } else { updateBtnText(); isRegister = !isRegister; @@ -114,14 +119,20 @@ function submitDetails(evt) { } if(new_password) { if(repeat_new_password !== new_password) { - evt.target['repeat-new-password'].classList.add('error'); + showMessage("Passwords are not same!", { type: 'err' }) return; } submit_values.password = new_password; } updateUserInfo(submit_values).then(res=>{ - // TODO: show updated or not - console.log(res) + if(res) { + submit_values.email && showMessage('Email updated.'); + submit_values.password && showMessage('Password updated.'); + evt.target['new-password'].value = '' + evt.target['repeat-new-password'].value = '' + } else { + showMessage('Update information failed!', { type: 'err' }) + } }); } else if(isRegister) { const username = evt.target.username.value; @@ -130,23 +141,30 @@ function submitDetails(evt) { const repeat_password = evt.target['repeat-password'].value; if(password !== repeat_password) { - evt.target['repeat-password'].classList.add('error'); + showMessage("Passwords are not same!", { type: 'err' }) return; } - register(username, email, password).then(()=>isRegister=false); + register(username, email, password).then(res=>{ + if(res) { + isRegister=false; + showMessage('Register Success!', { type: 'success' }); + } else showMessage('Register failed!', { type: 'err' }) + }); } else { const username = evt.target.username.value; const password = evt.target.password.value; - login(username, password); + login(username, password).then(res=>{ + if(res) showMessage(`Welcome back, ${username}`); + else showMessage('Login failed!', { type: 'err' }) + }); } } export default function createAccountPage() { if(toggleDialog()) return; - account_dialog = document.createElement('dialog'); - account_dialog.onclick = () => toggleDialog(false); - document.getElementById('user-popup-container').appendChild(account_dialog) + account_container = document.getElementById('user-popup-container'); + account_container.onclick = () => toggleDialog(false); const account_main_page = document.createElement('form'); account_main_page.className = 'account-main'; @@ -160,9 +178,11 @@ export default function createAccountPage() { input_details_main.className = 'input-details-main'; account_main_page.appendChild(input_details_main); - account_dialog.appendChild(account_main_page); + account_container.appendChild(account_main_page) createInputDetailsPage(); + + init = true; toggleDialog(); } From 8e1bd88429bdfa0d3e8642cd7bcc94adf2c7f363 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:30:45 +1000 Subject: [PATCH 23/34] fix bugs user cannot click Signed-off-by: cbh778899 --- tools/message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/message.js b/tools/message.js index 41ca40b..2b50e12 100644 --- a/tools/message.js +++ b/tools/message.js @@ -3,7 +3,7 @@ import getSVG from "./svgs.js"; const message_dialog = document.createElement('dialog'); message_dialog.className = 'message-dialog'; document.body.appendChild(message_dialog); -message_dialog.showModal(); +message_dialog.show(); /** * @typedef messageOptions From 40574ee02207a8121b97bc98e8c0937acab84cf4 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:35:05 +1000 Subject: [PATCH 24/34] updateall after update info Signed-off-by: cbh778899 --- global/useUser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/global/useUser.js b/global/useUser.js index f7368a4..f9b7938 100644 --- a/global/useUser.js +++ b/global/useUser.js @@ -90,6 +90,7 @@ export default function useUser(updated) { ...userInfo, id, email: authorizedAccount.email } + updateAll(userInfo); return true; } return false; } From 4fc0f1466c0b5e6283a602cd15a45477481b95ce Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 19:41:28 +1000 Subject: [PATCH 25/34] fix bugs when user switched page and cannot toggle sidebar expand Signed-off-by: cbh778899 --- components/chat-page/chatMain.js | 33 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/components/chat-page/chatMain.js b/components/chat-page/chatMain.js index 231f764..15aee14 100644 --- a/components/chat-page/chatMain.js +++ b/components/chat-page/chatMain.js @@ -2,12 +2,13 @@ import useConversation from "../../global/useConversation.js"; import useHistory from "../../global/useHistory.js"; import useModelSettings from "../../global/useModelSettings.js"; import { formatJSON } from "../../tools/conversationFormat.js"; +import showMessage from "../../tools/message.js"; import request from "../../tools/request.js"; import getSVG from "../../tools/svgs.js"; let conversation = {}, model_settings = {}, main_elem, toggle_expand, - stream_response=true, init = false; + stream_response=true; const { componetDismount: conversationDismount, @@ -75,21 +76,17 @@ export default function createChatMain(main, toggleExpand, openModelSetting) { `) - if(!init) { - toggle_expand = toggleExpand; - document.getElementById('submit-chat').onsubmit=submitContent; - main_elem = document.getElementById('conversation-main'); - document.getElementById('toggle-sidebar-expand').onclick = toggle_expand; - document.getElementById('toggle-setting-page').onclick = openModelSetting; - document.getElementById('download-conversation').onclick = () => { - if(conversation.id && conversation.history.length) { - formatJSON(conversation, getHistory(conversation.id)) - } + toggle_expand = toggleExpand; + document.getElementById('submit-chat').onsubmit=submitContent; + main_elem = document.getElementById('conversation-main'); + document.getElementById('toggle-sidebar-expand').onclick = toggle_expand; + document.getElementById('toggle-setting-page').onclick = openModelSetting; + document.getElementById('download-conversation').onclick = () => { + if(conversation.id && conversation.history.length) { + formatJSON(conversation, getHistory(conversation.id)) } - init = true; - } else { - modelSettingsRemount(); } + modelSettingsRemount(); if(conversationReMount() && conversation.id) { updateConversation(); @@ -126,7 +123,13 @@ function buildForm() { function submitContent(evt) { evt.preventDefault(); - if(conversation.pending) return; + if(conversation.pending) { + showMessage( + "Please wait until assistant finished response.", + { type: 'warn' } + ) + return; + } const content = evt.target['send-content'].value; content && ( From c7571c92054f4f4325c34e77e1facf9612f7f415 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 21:35:52 +1000 Subject: [PATCH 26/34] update style Signed-off-by: cbh778899 --- styles/account_page.css | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/styles/account_page.css b/styles/account_page.css index 3ac69f4..e6e92e0 100644 --- a/styles/account_page.css +++ b/styles/account_page.css @@ -23,6 +23,7 @@ top: 10vh; background-color: white; border-radius: 15px; + overflow-x: hidden; } .account-main .logo-image { @@ -40,14 +41,14 @@ } .account-main .input-details-main { - --item-width: 230px; + --item-width: 250px; --item-height: 45px; position: absolute; - width: calc(var(--details-width) - 20px); + width: var(--item-width); height: calc(100% - 40px); top: 0; - right: 0; + left: var(--img-width); align-content: center; overflow-y: auto; overflow-x: hidden; @@ -57,11 +58,10 @@ } .input-details-main .account-field-container { - width: var(--item-width); + width: 100%; height: var(--item-height); position: relative; margin-bottom: 15px; - max-width: 100%; border: 1px solid gray; border-radius: 5px; } @@ -98,7 +98,7 @@ .input-details-main .account-field-container input { width: 100%; - height: 30%; + height: 40%; border: none; box-sizing: border-box; padding: 0px 15px; @@ -122,6 +122,20 @@ } .password-eye-icon svg { margin: auto; } +.input-details-main .keep-login { + display: flex; + height: fit-content; + width: fit-content; + align-items: center; + margin: auto; +} +.input-details-main .keep-login * { margin: auto; } +.input-details-main .keep-login input[type='checkbox']{ + width: 17px; + height: 17px; + margin-right: 10px; +} + .input-details-main hr { width: var(--item-width); position: relative; From b632a9f9716116c58a6f4239ab37b448964d2aa6 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 21:36:09 +1000 Subject: [PATCH 27/34] implement save user info to local Signed-off-by: cbh778899 --- components/account-page/index.js | 98 ++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index 5ac20af..8aab94b 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -4,7 +4,8 @@ import getSVG from "../../tools/svgs.js"; import showMessage from "../../tools/message.js"; let account_container = null, input_details_main = null, init = false, open_status = false; -let current_user = {}, isRegister = false; +let current_user = {}, isRegister = false, user_info_saved = localStorage.getItem('saved-user-login-info'); +if(user_info_saved) user_info_saved = JSON.parse(user_info_saved); const { register, login, logout, updateUserInfo @@ -27,10 +28,20 @@ function toggleDialog(force = null) { return true; } +function saveUserInfo(save_info = null) { + if(!save_info) { + user_info_saved && localStorage.removeItem('saved-user-login-info'); + user_info_saved = null; + } else { + user_info_saved = save_info + localStorage.setItem('saved-user-login-info', JSON.stringify(save_info)) + } +} + const account_fields = { login: [ { index: 'username' }, - { index: 'password', type: 'password' }, + { index: 'password', type: 'password' } ], register: [ { index: 'username' }, @@ -54,21 +65,33 @@ function createInputDetailsPage() { isRegister ? 'register' : 'login' ] .forEach(e=>{ + const field = {...e}; if(current_user.logged_in) { - if(e.index === 'username') e.value = current_user.username; - else if(e.index === 'email') e.value = current_user.email; - } else if(!isRegister) { - const saved_user_login_info = localStorage.getItem('saved-user-login-info'); - if(saved_user_login_info) { - const info = JSON.parse(saved_user_login_info); - if(e.index === 'username') e.value = info.username; - else if(e.index === 'email') e.value = info.password; - } + if(e.index === 'username') field.value = current_user.username; + else if(e.index === 'email') field.value = current_user.email; + } else if(!isRegister && user_info_saved) { + if(e.index === 'username') field.value = user_info_saved.username; + else if(e.index === 'password') field.value = user_info_saved.password; } input_details_main.appendChild( - createAccountInputFields(e) + createAccountInputFields(field) ) }) + + if(!current_user.logged_in) { + const keep_login = document.createElement('div') + keep_login.className = 'keep-login clickable'; + + keep_login.innerHTML = ` +
Keep me logged in!
` + + input_details_main.appendChild(keep_login); + keep_login.onclick = () => { + keep_login.firstElementChild.click(); + } + } + // hr input_details_main.insertAdjacentHTML("beforeend", "
") // buttons @@ -96,7 +119,7 @@ function createInputDetailsPage() { functional_btn.onclick = evt => { evt.preventDefault(); if(current_user.logged_in) { - logout().then(()=>showMessage('User Logged Out.')); + logout().then(()=>showMessage('User logged out.')); } else { updateBtnText(); isRegister = !isRegister; @@ -108,7 +131,7 @@ function createInputDetailsPage() { function submitDetails(evt) { evt.preventDefault(); - + if(current_user.logged_in) { const email = evt.target.email.value; const new_password = evt.target['new-password'].value; @@ -134,30 +157,35 @@ function submitDetails(evt) { showMessage('Update information failed!', { type: 'err' }) } }); - } else if(isRegister) { - const username = evt.target.username.value; - const email = evt.target.email.value; - const password = evt.target.password.value; - const repeat_password = evt.target['repeat-password'].value; - - if(password !== repeat_password) { - showMessage("Passwords are not same!", { type: 'err' }) - return; - } - register(username, email, password).then(res=>{ - if(res) { - isRegister=false; - showMessage('Register Success!', { type: 'success' }); - } else showMessage('Register failed!', { type: 'err' }) - }); } else { const username = evt.target.username.value; const password = evt.target.password.value; - login(username, password).then(res=>{ - if(res) showMessage(`Welcome back, ${username}`); - else showMessage('Login failed!', { type: 'err' }) - }); - } + const keep_login = evt.target['keep-login'].checked; + if(isRegister) { + const email = evt.target.email.value; + const repeat_password = evt.target['repeat-password'].value; + + if(password !== repeat_password) { + showMessage("Passwords are not same!", { type: 'err' }) + return; + } + register(username, email, password).then(res=>{ + if(res) { + isRegister=false; + showMessage('Register Success!', { type: 'success' }); + saveUserInfo(keep_login && { username, password }); + } else showMessage('Register failed!', { type: 'err' }); + }); + } else { + login(username, password).then(res=>{ + if(res) { + showMessage(`Welcome back, ${username}`); + saveUserInfo(keep_login && { username, password }); + } + else showMessage('Login failed!', { type: 'err' }) + }); + } + } } export default function createAccountPage() { From 6d29b7fbdfe8973a1a085fa64aee70ff0742e48a Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sat, 13 Jul 2024 21:48:47 +1000 Subject: [PATCH 28/34] update message background Signed-off-by: cbh778899 --- styles/message.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/styles/message.css b/styles/message.css index 100e7b9..b9a4a48 100644 --- a/styles/message.css +++ b/styles/message.css @@ -39,11 +39,12 @@ background-color: white; border: 1px solid; color: dodgerblue; + background-color: rgb(245, 250, 255); } -.message.err,.message.error { color: red; } -.message.warn,.message.warning { color: goldenrod; } -.message.success { color: limegreen; } +.message.err,.message.error { color: red; background-color: rgb(255, 244, 244); } +.message.warn,.message.warning { color: goldenrod; background-color: rgb(255, 254, 240); } +.message.success { color: limegreen; background-color: rgb(240, 253, 240); } .message-dialog .message .message-icon { width: 30px; From 23d08607354328ba8b6095ee13112a2000419b3a Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sun, 14 Jul 2024 11:03:36 +1000 Subject: [PATCH 29/34] add function to create a fake dialog, so we make sure messages showes on top Signed-off-by: cbh778899 --- index.html | 1 + styles/dialog.css | 22 +++++++++++++++ tools/dialog.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 styles/dialog.css create mode 100644 tools/dialog.js diff --git a/index.html b/index.html index 4c2119e..24346be 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,7 @@ Rebel + diff --git a/styles/dialog.css b/styles/dialog.css new file mode 100644 index 0000000..21fb501 --- /dev/null +++ b/styles/dialog.css @@ -0,0 +1,22 @@ +.mock-dialog { + position: fixed; + width: 100vw; + height: 100vh; + pointer-events: none; + display: none; + background-color: transparent; + z-index: 100; +} + +.mock-dialog * { + pointer-events: initial; +} + +.mock-dialog.show { + display: block; +} + +.mock-dialog.show.modal { + background-color: rgba(0, 0, 0, 0.1); + pointer-events: initial; +} \ No newline at end of file diff --git a/tools/dialog.js b/tools/dialog.js new file mode 100644 index 0000000..fe8e27e --- /dev/null +++ b/tools/dialog.js @@ -0,0 +1,68 @@ +let max_z_index = 100; +const displaying = []; + +function closeDialogListener(evt) { + if(evt.key === 'Escape') { + displaying.length && displaying.pop()(); + } +} + +document.addEventListener("keydown", closeDialogListener) + +export default function createDialog(modal_close_on_click = true) { + const dialog = document.createElement('div'); + dialog.className = 'mock-dialog'; + + let open_status = false, opened_idx = -1; + + function close(manual = true) { + open_status = false; + dialog.classList.remove('show', 'modal'); + if(manual) { + if(opened_idx >= 0) displaying.splice(opened_idx, 1); + } + if(!displaying.length) { + max_z_index = 100; + } + opened_idx = -1; + dialog.removeAttribute('style') + } + + if(modal_close_on_click) dialog.onclick = close; + + function display(modal = false) { + open_status = true; + dialog.classList.add('show'); + modal && dialog.classList.add('modal'); + max_z_index += 1; + dialog.style.zIndex = `${max_z_index}`; + opened_idx = displaying.length; + displaying[opened_idx] = close; + } + + function show() { + display(); + } + + function showModal() { + display(true); + } + + function toggle() { + open_status = !open_status; + open_status ? show() : close(); + return open_status; + } + + function toggleModal() { + open_status = !open_status; + open_status ? showModal() : close(); + return open_status; + } + + function getStatus() { + return open_status; + } + + return [ dialog, { show, showModal, close, toggle, toggleModal, getStatus } ]; +} \ No newline at end of file From e3581a67ad6dce8a26d56606e4f6ae472fb9dc80 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sun, 14 Jul 2024 11:04:39 +1000 Subject: [PATCH 30/34] implement createDialog to replace dialogs Signed-off-by: cbh778899 --- components/account-page/index.js | 28 +++++--------- components/chat-page/index.js | 13 +++---- components/chat-page/modelSettings.js | 54 +++++++++++++-------------- 3 files changed, 42 insertions(+), 53 deletions(-) diff --git a/components/account-page/index.js b/components/account-page/index.js index 8aab94b..0bd4d6f 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -2,8 +2,9 @@ import useUser from '../../global/useUser.js' import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js"; import getSVG from "../../tools/svgs.js"; import showMessage from "../../tools/message.js"; +import createDialog from '../../tools/dialog.js'; -let account_container = null, input_details_main = null, init = false, open_status = false; +let input_details_main = null, init = false, toggleDialog; let current_user = {}, isRegister = false, user_info_saved = localStorage.getItem('saved-user-login-info'); if(user_info_saved) user_info_saved = JSON.parse(user_info_saved); @@ -15,19 +16,6 @@ const { createInputDetailsPage(); }); -function toggleDialog(force = null) { - if(!init) return false; - - open_status = force === null ? !open_status : force; - if(open_status) { - account_container.style.display = 'block'; - } else { - account_container.style.display = 'none'; - } - - return true; -} - function saveUserInfo(save_info = null) { if(!save_info) { user_info_saved && localStorage.removeItem('saved-user-login-info'); @@ -189,10 +177,13 @@ function submitDetails(evt) { } export default function createAccountPage() { - if(toggleDialog()) return; + if(init && toggleDialog) { + toggleDialog(); + return; + } - account_container = document.getElementById('user-popup-container'); - account_container.onclick = () => toggleDialog(false); + const [account_container, controller] = createDialog(); + toggleDialog = controller.toggleModal; const account_main_page = document.createElement('form'); account_main_page.className = 'account-main'; @@ -207,11 +198,12 @@ export default function createAccountPage() { account_main_page.appendChild(input_details_main); account_container.appendChild(account_main_page) + document.body.appendChild(account_container) createInputDetailsPage(); init = true; - toggleDialog(); + controller.showModal(); } function createAccountInputFields({ diff --git a/components/chat-page/index.js b/components/chat-page/index.js index 8c3696b..398876c 100644 --- a/components/chat-page/index.js +++ b/components/chat-page/index.js @@ -1,7 +1,11 @@ +import createDialog from "../../tools/dialog.js"; import createChatMain from "./chatMain.js"; import createChatHistory from "./history.js"; import createModelSettings from "./modelSettings.js"; +const [settings_main, { toggleModal }] = createDialog(); +document.body.appendChild(settings_main) + export default function createChatPage() { const chatPage = document.createElement('div'); chatPage.id = 'chat-page'; @@ -12,16 +16,11 @@ export default function createChatPage() { chatPage.classList.toggle('sidebar-expanded'); } - let model_setting_elem; - function openModelSetting() { - model_setting_elem && model_setting_elem.showModal(); - } - const dismount_components = [] dismount_components.push(createChatHistory(chatPage)); - dismount_components.push(createChatMain(chatPage, toggleExpand, openModelSetting)); - dismount_components.push(createModelSettings(chatPage, elem=>model_setting_elem = elem)); + dismount_components.push(createChatMain(chatPage, toggleExpand, toggleModal)); + dismount_components.push(createModelSettings(settings_main)); return () => { dismount_components.forEach(e=>e()); diff --git a/components/chat-page/modelSettings.js b/components/chat-page/modelSettings.js index 9afc1c0..0b776ab 100644 --- a/components/chat-page/modelSettings.js +++ b/components/chat-page/modelSettings.js @@ -1,7 +1,7 @@ import useModelSettings from "../../global/useModelSettings.js"; import settingSection from "./settingSection.js"; -let settings = {}; +let settings = {}, init = false; const { componentReMount, componetDismount, @@ -15,37 +15,35 @@ const fields = { n_predict: { title: 'N-Predict', valueRange: { min: 128, max: 512 } } } -export default function createModelSettings(main, passDialogElem) { +export default function createModelSettings(main) { componentReMount(); - const popup = document.createElement('dialog'); - popup.onclick = () => popup.close(); - main.insertAdjacentElement("beforeend", popup) - passDialogElem(popup) - - const model_settings = document.createElement('div'); - model_settings.className = 'model-settings'; - model_settings.onclick = event => event.stopPropagation(); - - model_settings.insertAdjacentHTML('afterbegin', ` -
Adjust Model Settings
-
Settings will be saved automatically
- `) - - for(const key in fields) { - const { title, valueRange } = fields[key]; - const [component, setter] = settingSection( - title, valueRange, - () => { setToDefault(key) && loadSettings() }, - value=>updateModelSettings(key, value) - ) - model_settings.appendChild(component); - fields[key].setValue = setter; + if(!init) { + const model_settings = document.createElement('div'); + model_settings.className = 'model-settings'; + model_settings.onclick = event => event.stopPropagation(); + + model_settings.insertAdjacentHTML('afterbegin', ` +
Adjust Model Settings
+
Settings will be saved automatically
+ `) + + for(const key in fields) { + const { title, valueRange } = fields[key]; + const [component, setter] = settingSection( + title, valueRange, + () => { setToDefault(key) && loadSettings() }, + value=>updateModelSettings(key, value) + ) + model_settings.appendChild(component); + fields[key].setValue = setter; + } + + main.appendChild(model_settings); + loadSettings(); + init = true; } - popup.appendChild(model_settings); - - loadSettings(); return componetDismount; } From 4ac162e51f51bfafb22c179238ca4169e7a89134 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sun, 14 Jul 2024 11:04:53 +1000 Subject: [PATCH 31/34] remove dialog global styles Signed-off-by: cbh778899 --- styles/index.css | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/styles/index.css b/styles/index.css index d808ac8..53c71e6 100644 --- a/styles/index.css +++ b/styles/index.css @@ -32,20 +32,6 @@ svg { display: block; } -dialog { - position: fixed; - width: 100vw; - height: 100vh; - top: 0; - left: 0; - border: none; - padding: 0; - margin: 0; - background-color: transparent; - outline: none; -} -dialog:focus-visible { outline: none !important; } - .clickable:hover { cursor: pointer; } From fb780c852816bcf750cb06b1cb6e0d9e970c7ea0 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sun, 14 Jul 2024 11:05:01 +1000 Subject: [PATCH 32/34] update dialog style Signed-off-by: cbh778899 --- styles/message.css | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/styles/message.css b/styles/message.css index b9a4a48..808af34 100644 --- a/styles/message.css +++ b/styles/message.css @@ -1,11 +1,18 @@ .message-dialog { - pointer-events: none; - z-index: 100; -} - -.message-dialog::backdrop { + position: fixed; background-color: transparent; + border: none; + top: 0; + right: 0; + width: 100vw; + height: 100vh; + pointer-events: none; + outline: none; + z-index: 1000; + padding: 0; + margin: 0; } +.message-dialog:focus-visible { outline: none !important; } .message-dialog * { pointer-events: initial; From f2ebefea0724621e4757dbe90eab5f63d28dbf33 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sun, 14 Jul 2024 11:10:24 +1000 Subject: [PATCH 33/34] update version Signed-off-by: cbh778899 --- settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.js b/settings.js index 0a40c2a..f6cddfe 100644 --- a/settings.js +++ b/settings.js @@ -1,2 +1,2 @@ -export const VERSION = '0.1.5' +export const VERSION = '0.1.6' export const API_ADDRESS = '/api' From 67333e3ff30b6c492da4f26d4ad1df096b5136d6 Mon Sep 17 00:00:00 2001 From: cbh778899 Date: Sun, 14 Jul 2024 11:48:45 +1000 Subject: [PATCH 34/34] fix event propagation bug Signed-off-by: cbh778899 --- components/account-page/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/components/account-page/index.js b/components/account-page/index.js index 0bd4d6f..5b4e415 100644 --- a/components/account-page/index.js +++ b/components/account-page/index.js @@ -78,6 +78,7 @@ function createInputDetailsPage() { keep_login.onclick = () => { keep_login.firstElementChild.click(); } + keep_login.firstElementChild.onclick = evt => evt.stopPropagation(); } // hr