mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Add chat bookmarking
This commit is contained in:
1
public/img/address-book-solid.svg
Normal file
1
public/img/address-book-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M96 0C60.7 0 32 28.7 32 64V448c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64H96zM208 288h64c44.2 0 80 35.8 80 80c0 8.8-7.2 16-16 16H144c-8.8 0-16-7.2-16-16c0-44.2 35.8-80 80-80zm-32-96a64 64 0 1 1 128 0 64 64 0 1 1 -128 0zM512 80c0-8.8-7.2-16-16-16s-16 7.2-16 16v64c0 8.8 7.2 16 16 16s16-7.2 16-16V80zM496 192c-8.8 0-16 7.2-16 16v64c0 8.8 7.2 16 16 16s16-7.2 16-16V208c0-8.8-7.2-16-16-16zm16 144c0-8.8-7.2-16-16-16s-16 7.2-16 16v64c0 8.8 7.2 16 16 16s16-7.2 16-16V336z"/></svg>
|
After Width: | Height: | Size: 740 B |
1
public/img/bookmark-solid.svg
Normal file
1
public/img/bookmark-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 48V487.7C0 501.1 10.9 512 24.3 512c5 0 9.9-1.5 14-4.4L192 400 345.7 507.6c4.1 2.9 9 4.4 14 4.4c13.4 0 24.3-10.9 24.3-24.3V48c0-26.5-21.5-48-48-48H48C21.5 0 0 21.5 0 48z"/></svg>
|
After Width: | Height: | Size: 419 B |
1
public/img/comment-dots-solid.svg
Normal file
1
public/img/comment-dots-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 448c141.4 0 256-93.1 256-208S397.4 32 256 32S0 125.1 0 240c0 45.1 17.7 86.8 47.7 120.9c-1.9 24.5-11.4 46.3-21.4 62.9c-5.5 9.2-11.1 16.6-15.2 21.6c-2.1 2.5-3.7 4.4-4.9 5.7c-.6 .6-1 1.1-1.3 1.4l-.3 .3 0 0 0 0 0 0 0 0c-4.6 4.6-5.9 11.4-3.4 17.4c2.5 6 8.3 9.9 14.8 9.9c28.7 0 57.6-8.9 81.6-19.3c22.9-10 42.4-21.9 54.3-30.6c31.8 11.5 67 17.9 104.1 17.9zM128 208a32 32 0 1 1 0 64 32 32 0 1 1 0-64zm128 0a32 32 0 1 1 0 64 32 32 0 1 1 0-64zm96 32a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
|
After Width: | Height: | Size: 727 B |
1
public/img/comment-medical-solid.svg
Normal file
1
public/img/comment-medical-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 448c141.4 0 256-93.1 256-208S397.4 32 256 32S0 125.1 0 240c0 45.1 17.7 86.8 47.7 120.9c-1.9 24.5-11.4 46.3-21.4 62.9c-5.5 9.2-11.1 16.6-15.2 21.6c-2.1 2.5-3.7 4.4-4.9 5.7c-.6 .6-1 1.1-1.3 1.4l-.3 .3 0 0 0 0 0 0 0 0c-4.6 4.6-5.9 11.4-3.4 17.4c2.5 6 8.3 9.9 14.8 9.9c28.7 0 57.6-8.9 81.6-19.3c22.9-10 42.4-21.9 54.3-30.6c31.8 11.5 67 17.9 104.1 17.9zM224 160c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v48h48c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H288v48c0 8.8-7.2 16-16 16H240c-8.8 0-16-7.2-16-16V272H176c-8.8 0-16-7.2-16-16V224c0-8.8 7.2-16 16-16h48V160z"/></svg>
|
After Width: | Height: | Size: 806 B |
1
public/img/repeat-solid.svg
Normal file
1
public/img/repeat-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.3.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 224c0 17.7 14.3 32 32 32s32-14.3 32-32c0-53 43-96 96-96H320v32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S320 19.1 320 32V64H160C71.6 64 0 135.6 0 224zm512 64c0-17.7-14.3-32-32-32s-32 14.3-32 32c0 53-43 96-96 96H192V352c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V448H352c88.4 0 160-71.6 160-160z"/></svg>
|
After Width: | Height: | Size: 705 B |
@ -49,6 +49,7 @@
|
||||
<script type="module" src="scripts/power-user.js"></script>
|
||||
<script type="module" src="scripts/kai-settings.js"></script>
|
||||
<script type="module" src="scripts/textgen-settings.js"></script>
|
||||
<script type="module" src="scripts/bookmarks.js"></script>
|
||||
|
||||
<title>Tavern.AI</title>
|
||||
</head>
|
||||
@ -789,7 +790,7 @@
|
||||
<h4>Active extensions</h4>
|
||||
<ul id="extensions_list">
|
||||
</ul>
|
||||
<p>Missing something? Press <a id="extensions_details" href="javascript:void">here</a> for more details!</p>
|
||||
<p>Missing something? Press <a id="extensions_details" href="javascript:void(null);">here</a> for more details!</p>
|
||||
</div>
|
||||
<div id="extensions_settings">
|
||||
<h3>Extension settings</h3>
|
||||
@ -1219,7 +1220,7 @@
|
||||
|
||||
<!-- templates for JS to reuse when needed -->
|
||||
<div id="message_template">
|
||||
<div class="mes" mesid="${count_view_mes}" ch_name="${characterName}" is_user="${mes.is_user}">
|
||||
<div class="mes" mesid="${count_view_mes}" ch_name="${characterName}" is_user="${mes.is_user}" is_system="${mes.is_system}">
|
||||
<div class="for_checkbox"></div><input type="checkbox" class="del_checkbox">
|
||||
<div class="avatar"><img src=""></div>
|
||||
<div class="swipe_left"><img src="img/swipe_left.png"></div>
|
||||
@ -1276,20 +1277,24 @@
|
||||
<div id="options_button">
|
||||
<div id="options">
|
||||
<div class="options-content">
|
||||
<a id="option_new_bookmark">
|
||||
<img class="svg_icon" alt="" src="img/bookmark-solid.svg">
|
||||
<span>Save bookmark</span>
|
||||
</a>
|
||||
<a id="option_start_new_chat">
|
||||
<img alt="" src="img/save_and_start_new_chat.png">
|
||||
<img class="svg_icon" alt="" src="img/comment-dots-solid.svg">
|
||||
<span>Start new chat</span></a>
|
||||
<a id="option_select_chat">
|
||||
<img alt="" src="img/book6.png">
|
||||
<img class="svg_icon" alt="" src="img/address-book-solid.svg">
|
||||
<span>View Past Chats</span>
|
||||
</a>
|
||||
<hr>
|
||||
<a id="option_delete_mes">
|
||||
<img alt="" src="img/del_mes.png">
|
||||
<img class="svg_icon" alt="" src="img/trash-can-solid.svg">
|
||||
<span>Delete messages</span>
|
||||
</a>
|
||||
<a id="option_regenerate">
|
||||
<img alt="" src="img/regenerate.png">
|
||||
<img class="svg_icon" alt="" src="img/repeat-solid.svg">
|
||||
<span>Regenerate</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -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: <a class="bookmark_link" file_name="{0}" href="javascript:void(null);">{0}</a>`,
|
||||
},
|
||||
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: <a class="bookmark_link" file_name="{0}" href="javascript:void(null);">{0}</a>`,
|
||||
},
|
||||
};
|
||||
|
||||
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;
|
||||
|
61
public/scripts/bookmarks.js
Normal file
61
public/scripts/bookmarks.js
Normal file
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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
|
||||
;
|
||||
});
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user