Skip to content

Commit

Permalink
Merge pull request #29 from SkywardAI/chat-ui-try-1
Browse files Browse the repository at this point in the history
new page style implemented
  • Loading branch information
cbh778899 committed Jul 10, 2024
2 parents 18ce373 + f8ac747 commit 37acf7e
Show file tree
Hide file tree
Showing 31 changed files with 519 additions and 313 deletions.
23 changes: 4 additions & 19 deletions components/account-page/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
export default function createAccountPage() {

document.getElementById('main').innerHTML = `
<div class='account-page'>
<div class='input-field'>
<div class='title'>Username</div>
<input type='text' id='account-input-username'>
</div>
<div class='input-field'>
<div class='title'>Email</div>
<input type='text' id='account-input-email'>
</div>
<div class='input-field'>
<div class='title'>Password</div>
<input type='text' id='account-input-password'>
</div>
<div class='submit-account-btn clickable'>Submit</div>
</div>`
import getSVG from "../../tools/svgs.js";

return null;
export default function createAccountPage() {
document.getElementById('user-avatar')
.insertAdjacentHTML("afterbegin", getSVG('person-fill'))
}
126 changes: 83 additions & 43 deletions components/chat-page/chatMain.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import useConversation from "../../global/useConversation.js";
import useModelSettings from "../../global/useModelSettings.js";
import request from "../../tools/request.js";
import getSVG from "../../tools/svgs.js";

let conversation = {}, main_elem;
let conversation = {}, model_settings = {}, main_elem, stream_response=true;

const {
componetDismount, componentReMount,
componetDismount: conversationDismount,
componentReMount: conversationReMount,
sendMessage:appendConversationMessage
} = useConversation(c=>{
if(c.id === conversation.id) return;
Expand All @@ -15,34 +18,61 @@ const {
buildForm();
})

export default function createChatMain(main) {
const {
componetDismount: modelSettingsDismount,
componentReMount: modelSettingsRemount
} = useModelSettings(s=>{
model_settings = s;
})

export default function createChatMain(main, toggleExpand, openModelSetting) {
main.insertAdjacentHTML('beforeend', `
<div id='chat-main'>
<div id='conversation-main'>
<div class='greeting'>
Please select a ticket or start a new conversation on left.
<div class='chat-outer-main'>
<div
id='toggle-sidebar-expand' class='clickable function-icon'
title="Show/Hide Tickets History"
>
${getSVG('window-sidebar')}
</div>
<div
id='toggle-setting-page' class='clickable function-icon'
title="Show Model Settings"
>
${getSVG('gear')}
</div>
<div id='chat-main'>
<div id='conversation-main'>
<div class='greeting'>
Please select a ticket or start a new conversation on left.
</div>
</div>
<form id='submit-chat' autocomplete="off"></form>
</div>
<form id='submit-chat' autocomplete="off"></form>
</div>`)

document.getElementById('submit-chat').onsubmit=submitContent;
main_elem = document.getElementById('conversation-main');
document.getElementById('toggle-sidebar-expand').onclick = toggleExpand;
document.getElementById('toggle-setting-page').onclick = openModelSetting;

if(componentReMount() && conversation.id) {
modelSettingsRemount();
if(conversationReMount() && conversation.id) {
updateConversation();
buildForm();
}

return componetDismount;
return ()=>{
conversationDismount();
modelSettingsDismount();
};
}

function buildForm() {
document.getElementById('submit-chat').innerHTML = `
<input type='text' name='send-content' placeholder='Ask anything here!'>
<div class='send'>
<input type='submit' class='submit-btn clickable'>
<img class='submit-icon' src='/medias/send.svg'>
${getSVG('send', 'submit-icon')}
</div>`;
}

Expand All @@ -51,7 +81,7 @@ function submitContent(evt) {

const content = evt.target['send-content'].value;
content && (
conversation.stream_response ?
stream_response ?
sendMessageStream(content) :
sendMessageWaiting(content)
)
Expand All @@ -63,16 +93,22 @@ async function sendMessage(message, send) {
main_elem.innerHTML = ''
}
main_elem.appendChild(createBlock('out', message)[0]);
const [bot_answer, bot_answer_message] = createBlock('in');
main_elem.scrollTo({
top: main_elem.scrollHeight,
behavior: 'smooth'
})
const [bot_answer, updateMessage] = createBlock('in');
main_elem.appendChild(bot_answer);
bot_answer.focus();

const response = await request('chat', {
method: 'POST',
body: { sessionUuid: conversation.id || "uuid", message }
body: {
sessionUuid: conversation.id || "uuid",
message, ...model_settings
}
})

const content = await send(response, bot_answer_message);
const content = await send(response, updateMessage);

appendConversationMessage([
{ type: 'out', message },
Expand All @@ -81,15 +117,15 @@ async function sendMessage(message, send) {
}

function sendMessageWaiting(msg) {
return sendMessage(msg, async (response, pending_elem) => {
return sendMessage(msg, async (response, updateMessage) => {
const { message } = await response.json();
pending_elem.textContent = message;
updateMessage(message)
return message;
})
}

async function sendMessageStream(msg) {
return sendMessage(msg, async (response, pending_elem) => {
return sendMessage(msg, async (response, updateMessage) => {
let resp_content = ''
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let pending_content = ''
Expand All @@ -102,14 +138,15 @@ async function sendMessageStream(msg) {
try {
const json = JSON.parse(splitted_content.shift().replace('data: ', ''))
resp_content += json.content;
pending_elem.textContent = resp_content;
updateMessage(resp_content);
pending_content = splitted_content.join('')
if(json.stop) break;
} catch(error) {
console.error(error);
}
}
}
return resp_content;
})
}

Expand All @@ -124,37 +161,40 @@ function updateConversation() {
conversation.history.forEach(({type, message})=>{
main_elem.appendChild(createBlock(type, message)[0])
})

if(conversation.history.slice(-1)[0].type === 'out') {
main_elem.appendChild(createBlock('in')[0])
}
}

function createBlock(type, message=null) {
function createBlock(type, msg = '') {
const block = document.createElement('div');
block.className = `conversation-block sender-${type}`;

const content = document.createElement('div')
content.className = 'content';
content.innerHTML = `
<div class='sender-name'>
From: ${type === 'in' ? 'AI' : 'You'}
</div>`
const message = document.createElement('div');
message.className = 'message';

const message_elem = document.createElement('div');
message_elem.className = 'message';
message_elem.innerHTML = message || "<img class='loading' src='/medias/arrow-clockwise.svg'>"
block.appendChild(message);

content.insertAdjacentElement("beforeend", message_elem);
block.appendChild(content);
if(type === 'in') {
message.innerHTML = `
${getSVG('circle-fill', 'dot-animation dot-1')}
${getSVG('circle-fill', 'dot-animation dot-2')}
${getSVG('circle-fill', 'dot-animation dot-3')}`

const avatar = `
<div class='avatar'>
${type === 'in' ? '<img src="/medias/robot.svg">' : '<img src="/medias/person.svg">'}
</div>`
block.insertAdjacentHTML("afterbegin", `<img class='avatar' src='/medias/SkywardAI.png'>`)
}

if(type === 'in') block.insertAdjacentHTML("afterbegin", avatar);
else if(type === 'out') block.insertAdjacentHTML("beforeend", avatar);
if(msg) {
message.textContent = msg;
}

return [block, message_elem];
return [
block,
(msg) => {
if(msg) {
message.textContent = msg;
main_elem.scrollTo({
top: main_elem.scrollHeight,
behavior: 'smooth'
})
}
}
]
}
3 changes: 1 addition & 2 deletions components/chat-page/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ function updateHistoryList() {
ticket.id = `history-ticket-${id}`
ticket.innerHTML =
`<div class='title'>${name}</div>
<div class='datetime'>${createdAt}</div>
<div class='preview'>this is a preview</div>`
<div class='datetime'>${createdAt}</div>`

ticket.onclick = () => {
selectConversation(id);
Expand Down
17 changes: 13 additions & 4 deletions components/chat-page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@ import createModelSettings from "./modelSettings.js";

export default function createChatPage() {
const chatPage = document.createElement('div');
chatPage.id = 'chat-page'

chatPage.id = 'chat-page';
chatPage.className = 'sidebar-expanded';
document.getElementById('main').appendChild(chatPage);

function toggleExpand() {
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));
dismount_components.push(createModelSettings(chatPage));
dismount_components.push(createChatMain(chatPage, toggleExpand, openModelSetting));
dismount_components.push(createModelSettings(chatPage, elem=>model_setting_elem = elem));

return () => {
dismount_components.forEach(e=>e());
Expand Down
53 changes: 25 additions & 28 deletions components/chat-page/modelSettings.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,55 @@
import useConversation from "../../global/useConversation.js";
import debounce from "../../tools/debounce.js";
import useModelSettings from "../../global/useModelSettings.js";
import settingSection from "./settingSection.js";

let settings = {}, updated_settings = {}, init = false;
let settings = {};

const {
componentReMount, componetDismount,
updateSettings:updateModelSettings, setToDefault
} = useModelSettings(s=>settings = s);

const fields = {
temperature: { title: 'Temperature', valueRange: { min: 0, max: 2, is_100_times: true } },
top_k: { title: 'Top-K', valueRange: { min: 0, max: 40 } },
top_p: { title: 'Top-P', valueRange: { min: 0, max: 0.9, is_100_times: true } },
n_predict: { title: 'N-Predict', valueRange: { min: 0, max: 128 } }
n_predict: { title: 'N-Predict', valueRange: { min: 128, max: 512 } }
}

const { componetDismount, updateSetting, componentReMount } = useConversation(c=>{
settings = c;
loadSettings();
})

const updateModelSettings = debounce(async ()=>{
if(!await updateSetting(updated_settings)) {
loadSettings();
// TODO: show failed notification
} else {
// TODO: show success notification
}
}, 500)

export default function createModelSettings(main) {
export default function createModelSettings(main, passDialogElem) {
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', "<div class='title'>Adjust Model Settings</div>")
model_settings.insertAdjacentHTML('afterbegin', `
<div class='title'>Adjust Model Settings</div>
<div class='sub-title'>Settings will be saved automatically</div>
`)

for(const key in fields) {
const { title, valueRange } = fields[key];
const [component, setter] = settingSection(title, valueRange, value=>{
updated_settings[key] = value;
updateModelSettings();
})
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);
popup.appendChild(model_settings);

init = true;
loadSettings();
return componetDismount;
}

function loadSettings() {
if(!init) return;

for(const key in fields) {
fields[key].setValue(settings[key]);
}
Expand Down
Loading

0 comments on commit 37acf7e

Please sign in to comment.