diff --git a/public/img/address-book-solid.svg b/public/img/address-book-solid.svg new file mode 100644 index 000000000..823632c96 --- /dev/null +++ b/public/img/address-book-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/bookmark-solid.svg b/public/img/bookmark-solid.svg new file mode 100644 index 000000000..8b37d3d8e --- /dev/null +++ b/public/img/bookmark-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/comment-dots-solid.svg b/public/img/comment-dots-solid.svg new file mode 100644 index 000000000..22ebd04e2 --- /dev/null +++ b/public/img/comment-dots-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/comment-medical-solid.svg b/public/img/comment-medical-solid.svg new file mode 100644 index 000000000..305c31f69 --- /dev/null +++ b/public/img/comment-medical-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/repeat-solid.svg b/public/img/repeat-solid.svg new file mode 100644 index 000000000..139b542cb --- /dev/null +++ b/public/img/repeat-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/index.html b/public/index.html index 13aa16337..d0511c10c 100644 --- a/public/index.html +++ b/public/index.html @@ -49,6 +49,7 @@ + Tavern.AI @@ -789,7 +790,7 @@

Active extensions

-

Missing something? Press here for more details!

+

Missing something? Press here for more details!

Extension settings

@@ -1219,7 +1220,7 @@
-
+
@@ -1276,20 +1277,24 @@
diff --git a/public/script.js b/public/script.js index 66a2798af..193d1c14e 100644 --- a/public/script.js +++ b/public/script.js @@ -102,6 +102,7 @@ export { online_status, main_api, api_server, + system_messages, nai_settings, token, name1, @@ -163,6 +164,8 @@ const system_message_types = { GROUP: "group", EMPTY: "empty", GENERIC: "generic", + BOOKMARK_CREATED: "bookmark_created", + BOOKMARK_BACK: "bookmark_back", }; const system_messages = { @@ -223,6 +226,22 @@ const system_messages = { is_name: true, mes: "Generic system message. User `text` parameter to override the contents", }, + bookmark_created: { + name: systemUserName, + force_avatar: system_avatar, + is_user: false, + is_system: true, + is_name: true, + mes: `Bookmark created! Click here to open the bookmark chat: {0}`, + }, + bookmark_back: { + name: systemUserName, + force_avatar: system_avatar, + is_user: false, + is_system: true, + is_name: true, + mes: `Click here to return to the original chat: {0}`, + }, }; const talkativeness_default = 0.5; @@ -711,9 +730,9 @@ function messageFormating(mes, ch_name, isSystem, forceAvatar) { return mes; } -function getMessageFromTemplate(mesId, characterName, isUser, avatarImg, bias) { +function getMessageFromTemplate(mesId, characterName, isUser, avatarImg, bias, isSystem) { const mes = $('#message_template .mes').clone(); - mes.attr({ 'mesid': mesId, 'ch_name': characterName, 'is_user': isUser }); + mes.attr({ 'mesid': mesId, 'ch_name': characterName, 'is_user': isUser, 'is_system': !!isSystem }); mes.find('.avatar img').attr('src', avatarImg); mes.find('.ch_name .name_text').text(characterName); mes.find('.mes_bias').html(bias); @@ -767,7 +786,7 @@ function addOneMessage(mes, type = "normal") { ); const bias = messageFormating(mes.extra?.bias ?? ""); - var HTMLForEachMes = getMessageFromTemplate(count_view_mes, characterName, mes.is_user, avatarImg, bias); + var HTMLForEachMes = getMessageFromTemplate(count_view_mes, characterName, mes.is_user, avatarImg, bias, isSystem); if (type !== 'swipe') { $("#chat").append(HTMLForEachMes); @@ -1686,7 +1705,8 @@ function resultCheckStatusNovel() { $("#api_button_novel").css("display", "inline-block"); } -async function saveChat() { +async function saveChat(chat_name) { + let file_name = chat_name ?? characters[this_chid].chat; chat.forEach(function (item, i) { if (item["is_group"]) { alert('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.'); @@ -1714,7 +1734,7 @@ async function saveChat() { url: "/savechat", data: JSON.stringify({ ch_name: characters[this_chid].name, - file_name: characters[this_chid].chat, + file_name: file_name, chat: save_chat, avatar_url: characters[this_chid].avatar, }), @@ -3318,6 +3338,9 @@ $(document).ready(function () { processData: false, success: function (html) { $(".mes").each(function () { + if ($(this).attr("is_system") == 'true') { + return; + } if ($(this).attr("ch_name") != name1) { $(this) .children(".avatar") @@ -4035,7 +4058,7 @@ $(document).ready(function () { selectRightMenuWithAnimation('rm_extensions_block'); }); - $(document).on("click", ".select_chat_block", function () { + $(document).on("click", ".select_chat_block, .bookmark_link", function () { let file_name = $(this).attr("file_name").replace(".jsonl", ""); //console.log(characters[this_chid]['chat']); characters[this_chid]["chat"] = file_name; diff --git a/public/scripts/bookmarks.js b/public/scripts/bookmarks.js new file mode 100644 index 000000000..9ea56c585 --- /dev/null +++ b/public/scripts/bookmarks.js @@ -0,0 +1,61 @@ +import { + characters, + saveChat, + sendSystemMessage, + deleteLastMessage, + token, + system_messages, + system_message_types, + this_chid, +} from "../script.js"; +import { selected_group } from "./group-chats.js"; + +import { + stringFormat, +} from "./utils.js"; + +async function getExistingChatNames() { + const response = await fetch("/getallchatsofcharacter", { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + "X-CSRF-Token": token, + }, + body: JSON.stringify({ avatar_url: characters[this_chid].avatar}) + }); + + if (response.ok) { + const data = await response.json(); + return Object.values(data).map(x => x.file_name.replace('.jsonl', '')); + } +} + +async function getBookmarkName() { + const chatNames = await getExistingChatNames(); + let newChat = Date.now(); + + for (let i = 0; i < 1000; i++) { + newChat = `Bookmark - ${i}`; + if (!chatNames.includes(newChat)) { + break; + } + } + return newChat; +} + +$(document).ready(function () { + $('#option_new_bookmark').on('click', async function () { + if (selected_group) { + alert('Unsupported for groups'); + throw new Error('not yet implemented'); + } + + let newChat = await getBookmarkName(); + + saveChat(newChat); + let mainMessage = stringFormat(system_messages[system_message_types.BOOKMARK_CREATED].mes, newChat); + sendSystemMessage(system_message_types.BOOKMARK_CREATED, mainMessage); + saveChat(); + }); +}); + diff --git a/public/scripts/utils.js b/public/scripts/utils.js index 321d1e12d..63c53ac80 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -8,6 +8,8 @@ export { debounce, delay, isSubsetOf, + incrementString, + stringFormat, }; /// UTILS @@ -86,3 +88,22 @@ function debounce(func, timeout = 300) { const delay = (ms) => new Promise((res) => setTimeout(res, ms)); const isSubsetOf = (a, b) => (Array.isArray(a) && Array.isArray(b)) ? b.every(val => a.includes(val)) : false; + +function incrementString(str) { + // Find the trailing number or it will match the empty string + const count = str.match(/\d*$/); + + // Take the substring up until where the integer was matched + // Concatenate it to the matched count incremented by 1 + return str.substr(0, count.index) + (++count[0]); +}; + +function stringFormat(format) { + const args = Array.prototype.slice.call(arguments, 1); + return format.replace(/{(\d+)}/g, function (match, number) { + return typeof args[number] != 'undefined' + ? args[number] + : match + ; + }); +}; \ No newline at end of file diff --git a/public/style.css b/public/style.css index c1fe4fea2..bc3a16b9b 100644 --- a/public/style.css +++ b/public/style.css @@ -1970,7 +1970,8 @@ input[type="range"]{ position: absolute; width: 100%; height: 100vh; - z-index: 2059; + z-index: 3001; + top: 0; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }