diff --git a/public/index.html b/public/index.html index 508a5587b..7a718c487 100644 --- a/public/index.html +++ b/public/index.html @@ -41,13 +41,14 @@ const VERSION = '1.2.8'; var converter = new showdown.Converter(); var bg_menu_toggle = false; - const systemUserName = 'Chloe'; + const systemUserName = 'TavernAI'; + const systemCharName = 'Chloe'; var default_user_name = "You"; var name1 = default_user_name; - var name2 = systemUserName; + var name2 = systemCharName; // might want to migrate this to 'system message' code var chat = [{ - name: systemUserName, + name: systemCharName, is_user: false, is_name: true, create_date: 0, @@ -75,12 +76,13 @@ HELP: 'help', WELCOME: 'welcome', GROUP: 'group', + EMPTY: 'empty', }; const system_messages = { 'help': { "name": systemUserName, - "force_avatar": "img/chloe.png", + "force_avatar": "img/five.png", "is_user":false, "is_system": true, "is_name":true, @@ -88,11 +90,19 @@ }, 'group': { "name": systemUserName, - "force_avatar": "img/chloe.png", + "force_avatar": "img/five.png", "is_user":false, "is_system": true, "is_name": true, "mes": "Group chat created. Say 'Hi' to lovely people!" + }, + 'empty': { + "name": systemUserName, + "force_avatar": "img/five.png", + "is_user": false, + "is_system": true, + "is_name": true, + "mes": 'No one hears you. **Hint:** add more members to the group!' } }; @@ -100,6 +110,7 @@ 'before': 0, 'after': 1, } + const talkativeness_default = 0.5; var is_advanced_char_open = false; var is_world_edit_open = false; @@ -114,6 +125,7 @@ var create_save_avatar = ''; var create_save_scenario = ''; var create_save_mes_example = ''; + var create_save_talkativeness = talkativeness_default; var timerSaveEdit; var timerWorldSave; @@ -136,6 +148,7 @@ var is_api_button_press_novel = false; var is_send_press = false;//Send generation + let is_group_generating = false; // Group generation flag var add_mes_without_animation = false; var this_del_mes = 0; @@ -162,7 +175,7 @@ var max_context = 2048;//2048; var rep_pen = 1; var rep_pen_size = 100; - + var is_pygmalion = false; var tokens_already_generated = 0; var message_already_generated = ''; @@ -445,14 +458,82 @@ $("#rm_print_characters_block").prepend('
'+item.name+'
'); //console.log(item.name); }); + printGroups(); + } + + function printGroups() { for (let group of groups) { const template = $('#group_list_template .group_select').clone(); template.data('id', group.id); - template.find('.avatar img').attr('src', group.avatar_url); template.find('.ch_name').html(group.name); $('#rm_print_characters_block').prepend(template); + updateGroupAvatar(group); } } + + function updateGroupAvatar(group) { + $('#rm_print_characters_block .group_select').each(function() { + if ($(this).data('id') == group.id) { + const avatar = getGroupAvatar(group); + if (avatar) { + $(this).find('.avatar').replaceWith(avatar); + } + } + }) + } + + function getGroupAvatar(group) { + const memberAvatars = []; + if (group && Array.isArray(group.members) && group.members.length) { + for (const member of group.members) { + const charIndex = characters.findIndex(x => x.name === member); + if (charIndex !== -1 && characters[charIndex].avatar !== 'none') { + const this_avatar = `characters/${characters[charIndex].avatar}#${Date.now()}`; + memberAvatars.push(this_avatar); + } + if (memberAvatars.length === 4) { + break; + } + } + } + + // Cohee: there's probably a smarter way to do this.. + if (memberAvatars.length === 1) { + const groupAvatar = $('#group_avatars_template .collage_1').clone(); + groupAvatar.find('.img_1').attr('src', memberAvatars[0]); + return groupAvatar; + } + + if (memberAvatars.length === 2) { + const groupAvatar = $('#group_avatars_template .collage_2').clone(); + groupAvatar.find('.img_1').attr('src', memberAvatars[0]); + groupAvatar.find('.img_2').attr('src', memberAvatars[1]); + return groupAvatar; + } + + if (memberAvatars.length === 3) { + const groupAvatar = $('#group_avatars_template .collage_3').clone(); + groupAvatar.find('.img_1').attr('src', memberAvatars[0]); + groupAvatar.find('.img_2').attr('src', memberAvatars[1]); + groupAvatar.find('.img_3').attr('src', memberAvatars[2]); + return groupAvatar; + } + + if (memberAvatars.length === 4) { + const groupAvatar = $('#group_avatars_template .collage_4').clone(); + groupAvatar.find('.img_1').attr('src', memberAvatars[0]); + groupAvatar.find('.img_2').attr('src', memberAvatars[1]); + groupAvatar.find('.img_3').attr('src', memberAvatars[2]); + groupAvatar.find('.img_4').attr('src', memberAvatars[3]); + return groupAvatar; + } + + // default avatar + const groupAvatar = $('#group_avatars_template .collage_1').clone(); + groupAvatar.find('.img_1').attr('src', group.avatar_url); + return groupAvatar; + } + async function getCharacters() { await getGroups(); const response = await fetch("/getcharacters", { @@ -618,7 +699,7 @@ count_view_mes = 0; $('#chat').html(''); } - function messageFormating(mes, ch_name, isSystem){ + function messageFormating(mes, ch_name, isSystem, forceAvatar){ if(this_chid != undefined && !isSystem) mes = mes.replaceAll("<", "<").replaceAll(">", ">");//for Chloe if(this_chid === undefined){ mes = mes.replace(/\*\*(.+?)\*\*/g, '$1').replace(/\*(.+?)\*/g, '$1').replace(/\n/g, '
'); @@ -630,6 +711,9 @@ mes = mes.trim(); } + if (forceAvatar) { + mes = mes.replaceAll(ch_name+":", ""); + } if(ch_name !== name1){ mes = mes.replaceAll(name2+":", ""); @@ -681,7 +765,7 @@ messageText = messageText.replace(//gi, name1); messageText = messageText.replace(//gi, name2); } - messageText = messageFormating(messageText, characterName, isSystem); + messageText = messageFormating(messageText, characterName, isSystem, mes.force_avatar); const bias = messageFormating(mes.extra?.bias ?? ''); $("#chat").append( "
"+characterName+"
"+`
${bias}
` ); @@ -849,6 +933,18 @@ async function Generate(type) {//encode("dsfs").length tokens_already_generated = 0; message_already_generated = name2+': '; + + if (isHelpRequest($("#send_textarea").val())) { + sendSystemMessage(system_message_types.HELP); + $("#send_textarea").val('').trigger('input'); + return; + } + + if (selected_group && !is_group_generating) { + generateGroupWrapper(); + return; + } + if(online_status != 'no_connection' && this_chid != undefined){ if(type != 'regenerate'){ var textareaText = $("#send_textarea").val(); @@ -865,11 +961,6 @@ } } //$("#send_textarea").attr("disabled","disabled"); - - if (isHelpRequest(textareaText)) { - sendSystemMessage(system_message_types.HELP); - return; - } //$("#send_textarea").blur(); $( "#send_but" ).css("display", "none"); @@ -1348,6 +1439,24 @@ getMessage = getMessage.substr(0,getMessage.indexOf('<|endoftext|>')); } + // clean-up group message from excessive generations + if (type == 'group_chat' && selected_group) { + const group = groups.find(x => x.id == selected_group); + + if (group && Array.isArray(group.members) && group.members) { + for (let member of group.members) { + // Skip current speaker. + if (member === name2) { + continue; + } + + const indexOfMember = getMessage.indexOf(member+":"); + if (indexOfMember != -1) { + getMessage = getMessage.substr(0, indexOfMember); + } + } + } + } let this_mes_is_name = true; if(getMessage.indexOf(name2+":") === 0){ getMessage = getMessage.replace(name2+':', ''); @@ -1365,10 +1474,25 @@ chat[chat.length-1]['send_date'] = Date.now(); getMessage = $.trim(getMessage); chat[chat.length-1]['mes'] = getMessage; + + if (type === 'group_chat') { + let avatarImg = 'img/fluffy.png'; + if (characters[this_chid].avatar != 'none'){ + avatarImg = `characters/${characters[this_chid].avatar}`; + } + chat[chat.length-1]['is_name'] = true; + chat[chat.length-1]['force_avatar'] = avatarImg; + } + addOneMessage(chat[chat.length-1]); $( "#send_but" ).css("display", "inline"); $( "#loading_mes" ).css("display", "none"); - saveChat(); + + if (type == 'group_chat' && selected_group) { + saveGroupChat(selected_group); + } else { + saveChat(); + } }else{ //console.log('run force_name2 protocol'); Generate('force_name2'); @@ -1563,17 +1687,18 @@ const id = $(this).data('id'); selected_button = 'group_chats'; - if (selected_group !== id){ - if(!is_send_press){ - selected_group = id; - this_edit_mes_id = undefined; - clearChat(); - chat.length = 0; - await getGroupChat(id); - } - } + if(!is_send_press && !is_group_generating){ + if (selected_group !== id) { + selected_group = id; + this_chid = undefined; + this_edit_mes_id = undefined; + clearChat(); + chat.length = 0; + await getGroupChat(id); + } - select_group_chats(id); + select_group_chats(id); + } }); $("#rm_button_group_chats").click(function() { selected_button = 'group_chats'; @@ -1630,6 +1755,126 @@ $('#rm_info_block').transition({ opacity: 1.0 ,duration: 2000}); } }); + async function generateGroupWrapper() { + $('#chat .typing_indicator').remove(); + + if (online_status === 'no_connection') { + is_group_generating = false; + is_send_press = false; + return; + } + + const group = groups.find(x => x.id === selected_group); + + if (!group || !Array.isArray(group.members) || !group.members.length) { + sendSystemMessage(system_message_types.EMPTY); + return; + } + + try { + is_group_generating = true; + this_chid = undefined; + name2 = ''; + const userInput = $("#send_textarea").val(); + const activatedMembers = activateMembers(group.members, userInput); + let messagesBefore = chat.length; + + if (userInput && userInput.length) { + messagesBefore++; + } + + // now the real generation begins: cycle through every character + for (const chId of activatedMembers) { + this_chid = chId; + name2 = characters[chId].name; + + const typingIndicator = $('#typing_indicator_template .typing_indicator').clone(); + typingIndicator.find('.typing_indicator_name').text(characters[chId].name); + + await Generate('group_chat'); + + $('#chat').append(typingIndicator); + + while (true) { + // check if message generated already + if (chat.length == messagesBefore) { + await delay(10); + } else { + messagesBefore++; + break; + } + } + + $('#chat .typing_indicator').remove(); + } + + } finally { + is_group_generating = false; + is_send_press = false; + $('#chat .typing_indicator').remove(); + } + } + function activateMembers(members, input) { + let activatedNames = []; + + // find mentions + if (input && input.length) { + for (let inputWord of extractAllWords(input)) { + for (let member of members) { + if (extractAllWords(member).includes(inputWord)) { + activatedNames.push(member); + break; + } + } + } + } + + // activation by talkativeness + for (let member of members) { + const character = characters.find(x => x.name === member); + + if (!character) { + continue; + } + + const rollValue = Math.random(); + let talkativeness = Number(character.talkativeness); + talkativeness = Number.isNaN(talkativeness) ? talkativeness_default : talkativeness; + if (talkativeness > rollValue) { + activatedNames.push(member); + } + } + + // pick 1 at random if no one was activated + if (activatedNames.length === 0) { + const randomIndex = Math.floor(Math.random() * members.length); + activatedNames.push(members[randomIndex]); + } + + // de-duplicate array of names + function onlyUnique(value, index, array) { + return array.indexOf(value) === index; + } + activatedNames = activatedNames.filter(onlyUnique); + + console.log(activatedNames); + // map to character ids + const memberIds = activatedNames.map(x => characters.findIndex(y => y.name === x)).filter(x => x !== -1); + return memberIds; + } + function extractAllWords(value) { + const words = []; + + if (!value) { + return words; + } + + const matches = value.matchAll(/\b\w+\b/gmi); + for (let match of matches) { + words.push(match[0].toLowerCase()); + } + return words; + } async function getGroupChat(id) { const response = await fetch('/getgroupchat', { method: 'POST', @@ -1777,6 +2022,7 @@ group.members.push(id); } await editGroup(chat_id); + updateGroupAvatar(group); } $(this).remove(); @@ -1872,6 +2118,7 @@ $("#description_textarea").val(create_save_description); $("#personality_textarea").val(create_save_personality); $("#firstmessage_textarea").val(create_save_first_message); + $("#talkativeness_slider").val(create_save_talkativeness); $("#scenario_pole").val(create_save_scenario); if($.trim(create_save_mes_example).length == 0){ $("#mes_example_textarea").val(''); @@ -1949,6 +2196,7 @@ $("#personality_textarea").val(characters[chid].personality); $("#firstmessage_textarea").val(characters[chid].first_mes); $("#scenario_pole").val(characters[chid].scenario); + $("#talkativeness_slider").val(characters[chid].talkativeness ?? talkativeness_default); $("#mes_example_textarea").val(characters[chid].mes_example); $("#selected_chat_pole").val(characters[chid].chat); $("#create_date_pole").val(characters[chid].create_date); @@ -1966,9 +2214,10 @@ $("#form_create").attr("actiontype", "editcharacter"); } $(document).on('click', '.character_select', function(){ - selected_group = null; if(this_chid !== $(this).attr("chid")){ if(!is_send_press){ + selected_group = null; + is_group_generating = false; this_edit_mes_id = undefined; selected_button = 'character_edit'; this_chid = $(this).attr("chid"); @@ -2329,6 +2578,8 @@ create_save_personality = ''; $("#firstmessage_textarea").val(''); create_save_first_message = ''; + $("#talkativeness_slider").val(talkativeness_default); + create_save_talkativeness = talkativeness_default; $("#character_popup_text_h3").text('Create character'); @@ -2460,7 +2711,6 @@ }); $('#scenario_pole').on('keyup paste cut', function(){ if(menu_type == 'create'){ - create_save_scenario = $('#scenario_pole').val(); }else{ timerSaveEdit = setTimeout(() => {$("#create_button").click();},durationSaveEdit); @@ -2482,6 +2732,15 @@ timerSaveEdit = setTimeout(() => {$("#create_button").click();},durationSaveEdit); } }); + $('#talkativeness_slider').on('input', function() { + if (menu_type == 'create') { + create_save_talkativeness = $('#talkativeness_slider').val(); + } else { + timerSaveEdit = setTimeout(() => { + $('#create_button').click(); + }, durationSaveEdit); + } + }); $( "#api_button" ).click(function() { if($('#api_url_text').val() != ''){ $("#api_loading").css("display", 'inline-block'); @@ -2530,7 +2789,18 @@ }); } }); + function openNavToggle() { + if (!$('#nav-toggle').prop('checked')) { + $('#nav-toggle').trigger('click'); + } + } $( "#option_select_chat" ).click(function() { + if (selected_group) { + // will open a chat selection screen + openNavToggle(); + $("#rm_button_characters").trigger('click'); + return; + } if(this_chid != undefined && !is_send_press){ getAllCharaChats(); $('#shadow_select_chat_popup').css('display', 'block'); @@ -2539,6 +2809,12 @@ } }); $( "#option_start_new_chat" ).click(function() { + if (selected_group) { + // will open a group creation screen + openNavToggle(); + $("#rm_button_group_chats").trigger('click'); + return; + } if(this_chid != undefined && !is_send_press){ popup_type = 'new_chat'; callPopup('

Start new chat?

'); @@ -4052,6 +4328,17 @@
Circumstances and context of the dialogue (?)
+ +
+

Talkativeness

+
How often does the character speak randomly. Affects group chats only!
+ +
+ Shy + Normal + Chatty +
+

Examples of dialogue

@@ -4535,8 +4822,35 @@
+ +
+
CHAR is typing
+
+ +
+
+ img1 +
+
+ img1 + img2 +
+
+ img1 + img2 + img3 +
+
+ img1 + img2 + img3 + img4 +
+
+
-
+
+
diff --git a/public/style.css b/public/style.css index 9b9ea9358..a0353845c 100644 --- a/public/style.css +++ b/public/style.css @@ -1864,7 +1864,7 @@ label.checkbox :checked + span:after { background-color: rgba(0,0,0,0.3); backdrop-filter: blur(50px); -webkit-backdrop-filter: blur(50px); - grid-template-rows: 50px 100px 100px 40px auto 45px 45px; + grid-template-rows: 50px 100px 100px 110px 40px auto 45px 45px; max-width: 802px; /* 802 instead of 800 to cover #chat's scrollbars entirely */ height: 90vh; /* imperfect calculation designed to match the chat height, which is auto-set to (100% - form_sheld height) */ position: absolute; @@ -2635,4 +2635,177 @@ body { } .group_select .group_icon img { filter: invert(1); +} + +#typing_indicator_template { + display: none !important; +} + +.typing_indicator { + position: sticky; + bottom: 10px; + margin: 10px; + opacity: 0.6; + text-shadow: 2px 2px 2px rgba(0,0,0,0.6); +} + +.typing_indicator:after { + display: inline-block; + vertical-align: bottom; + animation: ellipsis steps(4,end) 1500ms infinite; + content: ""; /* ascii code for the ellipsis character */ + width: 0px; +} + +@keyframes ellipsis { + 0% { + content: "" + } + 25% { + content: "." + } + 50% { + content: ".." + } + 75% { + content: "..." + } +} + +#group_avatars_template { + display: none; +} + +.avatar_collage { + border-radius: 50%; + position: relative; + overflow: hidden; +} + +.avatar_collage img { + position: absolute; + overflow: hidden; + border-radius: 0% !important; +} + +.collage_2 .img_1 { + left: 0; + top: 0; + max-width: 50%; + max-height: 100%; + width: 50%; + height: 100%; +} + +.collage_2 .img_2 { + left: 50%; + top: 0; + max-width: 50%; + max-height: 100%; + width: 50%; + height: 100%; +} + +.collage_3 .img_1 { + left: 0; + top: 0; + max-width: 50%; + max-height: 50%; + width: 50%; + height: 50%; +} + +.collage_3 .img_2 { + left: 50%; + top: 0; + max-width: 50%; + max-height: 50%; + width: 50%; + height: 50%; +} + +.collage_3 .img_3 { + left: 0; + top: 50%; + max-width: 100%; + max-height: 50%; + width: 100%; + height: 50%; +} + +.collage_4 .img_1 { + left: 0; + top: 0; + max-width: 50%; + max-height: 50%; + width: 50%; + height: 50%; +} + +.collage_4 .img_2 { + left: 50%; + top: 0; + max-width: 50%; + max-height: 50%; + width: 50%; + height: 50%; +} + +.collage_4 .img_3 { + left: 0; + top: 50%; + max-width: 50%; + max-height: 50%; + width: 50%; + height: 50%; +} + +.collage_4 .img_4 { + left: 50%; + top: 50%; + max-width: 50%; + max-height: 50%; + width: 50%; + height: 50%; +} + +span.warning { + color:rgb(190, 0, 0); + font-weight: bolder; +} + +#talkativeness_hint { + display: flex; + flex-direction: row; + align-items: baseline; + justify-content: space-between; + width: 92%; +} + +#talkativeness_expander { + flex: 1; +} + +#talkativeness_div { + margin-left: 36px; + padding-top: 10px; +} + +#talkativeness_div input[type="range"] { + width: 92%; +} + +#talkativeness_hint span { + min-width: 10em; + font-size: small; +} + +#talkativeness_hint span:nth-child(1) { + text-align: left; +} +#talkativeness_hint span:nth-child(2) { + text-align: center; +} +#talkativeness_hint span:nth-child(3) { + text-align: right; } \ No newline at end of file diff --git a/server.js b/server.js index 4840eaf06..a0d727847 100644 --- a/server.js +++ b/server.js @@ -445,7 +445,7 @@ function checkServer(){ //***************** Main functions function charaFormatData(data){ - var char = {"name": data.ch_name, "description": data.description, "personality": data.personality, "first_mes": data.first_mes, "avatar": 'none', "chat": Date.now(), "mes_example": data.mes_example, "scenario": data.scenario, "create_date": Date.now()}; + var char = {"name": data.ch_name, "description": data.description, "personality": data.personality, "first_mes": data.first_mes, "avatar": 'none', "chat": Date.now(), "mes_example": data.mes_example, "scenario": data.scenario, "create_date": Date.now(), "talkativeness": data.talkativeness}; return char; } app.post("/createcharacter", urlencodedParser, function(request, response){