Add character tagging (groups pending)

This commit is contained in:
SillyLossy
2023-04-27 20:46:54 +03:00
parent 1d3fe7cfb5
commit 54804efa75
4 changed files with 365 additions and 111 deletions

View File

@ -1367,6 +1367,7 @@
<input id="extensions_url" type="text" class="text_pole" maxlength="250" /> <input id="extensions_url" type="text" class="text_pole" maxlength="250" />
<div class="extensions_url_block"> <div class="extensions_url_block">
<input id="extensions_connect" class="menu_button" type="submit" value="Connect" /> <input id="extensions_connect" class="menu_button" type="submit" value="Connect" />
<input id="extensions_details" class="menu_button" type="button" value="Manage extensions" />
<span class="expander"></span> <span class="expander"></span>
<label for="extensions_autoconnect"><input id="extensions_autoconnect" type="checkbox" /> <label for="extensions_autoconnect"><input id="extensions_autoconnect" type="checkbox" />
Auto-connect Auto-connect
@ -1379,8 +1380,6 @@
<h4>Active extensions</h4> <h4>Active extensions</h4>
<ul id="extensions_list"> <ul id="extensions_list">
</ul> </ul>
<p>Missing something? Press <a id="extensions_details" href="javascript:void(null);">here</a>
for more details!</p>
</div> </div>
<div id="extensions_settings"> <div id="extensions_settings">
<h3>Extension settings</h3> <h3>Extension settings</h3>
@ -1439,13 +1438,14 @@
<div id="delete_button" class="menu_button fa-solid fa-trash-can " title="Delete Character"></div> <div id="delete_button" class="menu_button fa-solid fa-trash-can " title="Delete Character"></div>
</div> </div>
</div> </div>
<div title="Token counts may be inaccurate and provided just for reference." id="result_info"></div> <div title="Token counts may be inaccurate and provided just for reference." id="result_info">&nbsp;</div>
</div> </div>
<hr> <hr>
<div id="tags_div" class="margin-bot-10px ui-widget"> <div id="tags_div">
<input id="tagInput" class="text_pole margin-bot-10px" /> <div class="margin-bot-10px">Tags</div>
<div id="tagList"></div> <input id="tagInput" class="text_pole" placeholder="Search / Create tags" />
<div id="tagList" class="tags"></div>
</div> </div>
<div id="description_div" class="margin-bot-10px"> <div id="description_div" class="margin-bot-10px">
@ -1529,35 +1529,6 @@
<h3>Members</h3> <h3>Members</h3>
<!-- !!!Don't break line after div!!! --> <!-- !!!Don't break line after div!!! -->
<div id="rm_group_members"></div> <div id="rm_group_members"></div>
<div id="group_member_template">
<div class="group_member">
<div class="avatar">
<img alt="Avatar" src="" />
</div>
<div class="ch_name"></div>
<div class="group_member_icon">
<div title="Move up" data-action="up" class="right_menu_button fa-solid fa-xl fa-chevron-up"></div>
<div title="Move down" data-action="down" class="right_menu_button fa-solid fa-xl fa-chevron-down"></div>
<div title="Remove from group" data-action="remove" class="right_menu_button fa-solid fa-2xl fa-xmark"></div>
<div title="Add to group" data-action="add" class="right_menu_button fa-solid fa-2xl fa-plus"></div>
</div>
</div>
</div>
<div id="group_list_template">
<div class="group_select">
<div class="avatar">
<img src="">
</div>
<div class="group_icon">
<div class="fa-solid fa-user-group"></div>
</div>
<div class="ch_name"></div>
<i class='group_fav_icon fa-solid fa-star fa-2xs'></i>
<input class="ch_fav" value="" hidden />
</div>
</div>
</div> </div>
<div id="rm_character_import" class="right_menu" style="display: none;"> <div id="rm_character_import" class="right_menu" style="display: none;">
@ -1586,6 +1557,9 @@
<option data-field="chat_size" data-order="asc">Least chatted</option> <option data-field="chat_size" data-order="asc">Least chatted</option>
</select> </select>
</form> </form>
<div id="rm_tag_filter" class="tags">
</div>
<div id="rm_print_characters_block"></div> <div id="rm_print_characters_block"></div>
</div> </div>
@ -1815,7 +1789,14 @@
<!-- templates for JS to reuse when needed --> <!-- templates for JS to reuse when needed -->
<div id="openai_logit_bias_template"> <div id="tag_template" class="template_element">
<span id="" class="tag">
<span class="tag_name"></span>
<i class="fa-solid fa-circle-xmark tag_remove"></i>
</span>
</div>
<div id="openai_logit_bias_template" class="template_element">
<div class="openai_logit_bias_form"> <div class="openai_logit_bias_form">
<input class="openai_logit_bias_text text_pole" placeholder="type here..." /> <input class="openai_logit_bias_text text_pole" placeholder="type here..." />
<input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" /> <input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" />
@ -1823,7 +1804,7 @@
</div> </div>
</div> </div>
<div id="message_template"> <div id="message_template" class="template_element">
<div class="mes" mesid="${count_view_mes}" ch_name="${characterName}" is_user="${mes.is_user}" is_system="${mes.is_system}"> <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="for_checkbox"></div><input type="checkbox" class="del_checkbox">
<div class="avatar"><img src=""></div> <div class="avatar"><img src=""></div>
@ -1856,7 +1837,7 @@
</div> </div>
</div> </div>
</div> </div>
<div id="group_avatars_template"> <div id="group_avatars_template" class="template_element">
<div class="avatar avatar_collage collage_1"> <div class="avatar avatar_collage collage_1">
<img alt="img1" class="img_1" src=""> <img alt="img1" class="img_1" src="">
</div> </div>
@ -1877,8 +1858,37 @@
</div> </div>
</div> </div>
<div id="group_member_template" class="template_element">
<div class="group_member">
<div class="avatar">
<img alt="Avatar" src="" />
</div>
<div class="ch_name"></div>
<div class="group_member_icon">
<div title="Move up" data-action="up" class="right_menu_button fa-solid fa-xl fa-chevron-up"></div>
<div title="Move down" data-action="down" class="right_menu_button fa-solid fa-xl fa-chevron-down"></div>
<div title="Remove from group" data-action="remove" class="right_menu_button fa-solid fa-2xl fa-xmark"></div>
<div title="Add to group" data-action="add" class="right_menu_button fa-solid fa-2xl fa-plus"></div>
</div>
</div>
</div>
<div id="group_list_template" class="template_element">
<div class="group_select">
<div class="avatar">
<img src="">
</div>
<div class="group_icon">
<div class="fa-solid fa-user-group"></div>
</div>
<div class="ch_name"></div>
<i class='group_fav_icon fa-solid fa-star fa-2xs'></i>
<input class="ch_fav" value="" hidden />
</div>
</div>
<!-- chat and input bar --> <!-- chat and input bar -->
<div id="typing_indicator_template"> <div id="typing_indicator_template" class="template_element">
<div class="typing_indicator"><span class="typing_indicator_name">CHAR</span> is typing</div> <div class="typing_indicator"><span class="typing_indicator_name">CHAR</span> is typing</div>
</div> </div>
<div id="sheld"> <div id="sheld">

View File

@ -93,6 +93,7 @@ import {
import { debounce, delay } from "./scripts/utils.js"; import { debounce, delay } from "./scripts/utils.js";
import { extension_settings, loadExtensionSettings } from "./scripts/extensions.js"; import { extension_settings, loadExtensionSettings } from "./scripts/extensions.js";
import { executeSlashCommands, getSlashCommandsHelp } from "./scripts/slash-commands.js"; import { executeSlashCommands, getSlashCommandsHelp } from "./scripts/slash-commands.js";
import { tag_map, tags, loadTagsSettings, printTags, isElementTagged } from "./scripts/tags.js";
//exporting functions and vars for mods //exporting functions and vars for mods
export { export {
@ -277,6 +278,8 @@ const system_messages = {
'</ol>', '</ol>',
'Type <tt>/?</tt> in any chat to get help on message formatting commands.', 'Type <tt>/?</tt> in any chat to get help on message formatting commands.',
'<h3>Still have questions or suggestions left?</h3>', '<h3>Still have questions or suggestions left?</h3>',
'<a target="_blank" href="https://discord.gg/RZdyAEUPvj">SillyTavern Community Discord</a>',
'<br/>',
'<a target="_blank" href="https://github.com/Cohee1207/SillyTavern/issues">Post a GitHub issue.</a>', '<a target="_blank" href="https://github.com/Cohee1207/SillyTavern/issues">Post a GitHub issue.</a>',
'<br/>', '<br/>',
'<a target="_blank" href="https://github.com/Cohee1207/SillyTavern#questions-or-suggestions">Contact the developers.</a>' '<a target="_blank" href="https://github.com/Cohee1207/SillyTavern#questions-or-suggestions">Contact the developers.</a>'
@ -667,9 +670,8 @@ function printCharacters() {
this_avatar = getThumbnailUrl('avatar', item.avatar); this_avatar = getThumbnailUrl('avatar', item.avatar);
} //RossAscends: changed 'prepend' to 'append' to make alphabetical sorting display correctly. } //RossAscends: changed 'prepend' to 'append' to make alphabetical sorting display correctly.
$("#rm_print_characters_block").append( $("#rm_print_characters_block").append(
`<div class=character_select chid=${i} id="CharID${i}"> `<div class=character_select chid=${i} id="CharID${i}">
<div class=avatar><img src="${this_avatar}"></div> <div class=avatar title="${item.avatar}"><img src="${this_avatar}"></div>
<div class=ch_name>${item.name} ${item.fav == "true" ? '<i class="fa-solid fa-star fa-2xs"></i>' : ''}</div> <div class=ch_name>${item.name} ${item.fav == "true" ? '<i class="fa-solid fa-star fa-2xs"></i>' : ''}</div>
<input class="ch_fav" value=${item.fav} hidden /> <input class="ch_fav" value=${item.fav} hidden />
</div>` </div>`
@ -677,6 +679,7 @@ function printCharacters() {
//console.log('printcharacters() -- printing -- ChID '+i+' ('+item.name+')'); //console.log('printcharacters() -- printing -- ChID '+i+' ('+item.name+')');
}); });
$("#rm_print_characters_block").prepend(`<hr>`); $("#rm_print_characters_block").prepend(`<hr>`);
printTags();
printGroups(); printGroups();
sortCharactersList(); sortCharactersList();
} }
@ -2825,6 +2828,8 @@ async function getSettings(type) {
// Load power user settings // Load power user settings
loadPowerUserSettings(settings, data); loadPowerUserSettings(settings, data);
// Load- character tags
loadTagsSettings(settings);
//Enable GUI deference settings if GUI is selected for Kobold //Enable GUI deference settings if GUI is selected for Kobold
if (main_api === "kobold") { if (main_api === "kobold") {
@ -2918,10 +2923,12 @@ async function saveSettings(type) {
power_user: power_user, power_user: power_user,
poe_settings: poe_settings, poe_settings: poe_settings,
extension_settings: extension_settings, extension_settings: extension_settings,
tags: tags,
tag_map: tag_map,
...nai_settings, ...nai_settings,
...kai_settings, ...kai_settings,
...oai_settings, ...oai_settings,
}), }, null, 4),
beforeSend: function () { beforeSend: function () {
//console.log('saveSettings() -- active_character -- '+active_character); //console.log('saveSettings() -- active_character -- '+active_character);
if (type == "change_name") { if (type == "change_name") {
@ -3811,16 +3818,38 @@ $(document).ready(function () {
$("#character_search_bar").on("input", function () { $("#character_search_bar").on("input", function () {
const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(','); const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(',');
const searchValue = $(this).val().trim().toLowerCase(); const searchValue = $(this).val().trim().toLowerCase();
const selectedTagId = $('#rm_tag_filter .tag.selected').attr('id');
if (!searchValue) { if (!searchValue) {
$(selector).show(); $(selector).each(function () {
if (selectedTagId && !isElementTagged(this, selectedTagId)) {
$(this).hide();
}
else {
$(this).show();
}
})
} else { } else {
$(selector).each(function () { $(selector).each(function () {
$(this).children(".ch_name").text().toLowerCase().includes(searchValue) const isValidSearch = $(this).children(".ch_name").text().toLowerCase().includes(searchValue);
? $(this).show()
: $(this).hide(); if (isValidSearch) {
if (selectedTagId && !isElementTagged(this, selectedTagId)) {
$(this).hide();
}
else {
$(this).show();
}
}
else {
$(this).hide();
}
}); });
} }
if (selectedTagId) {
applyFilterToList(selectedTagId)
}
}); });
$("#filter_by_fav").click(function () { $("#filter_by_fav").click(function () {

View File

@ -1,49 +1,253 @@
import { characters, saveSettingsDebounced, this_chid } from "../script.js";
import { selected_group } from "./group-chats.js";
export {
tags,
tag_map,
loadTagsSettings,
printTags,
isElementTagged,
};
const TAG_COLORS = [ const TAG_COLORS = [
'#FFB6C1', // Light Pink "#dd0a36", // Red
'#FFECB3', // Light Yellow "#ff6633", // Orange
'#FFE4B5', // Cream "#5f9ea0", // Teal Green
'#B2EBF2', // Powder Blue "#1e90ff", // Light Blue
'#E0FFFF', // Light Sky Blue "#990066", // Plum
'#FBB4B9', // Lavender "#8c00ff", // Fuchsia
'#FFF9C4', // Floral White "#00ffff", // Aqua
'#DDA0DD', // Plum "#0f4ecc", // Teal
'#DA70D6', // Orchid "#2f4b1c", // Green
'#D2B48C', // Tan "#3366e5", // Dodger Blue
'#FAEBD7', // Antique White "#36c3a1", // Mint Green
'#FFEFD5', // Papaya Whip "#995511", // Terracotta
'#CD853F', // Peru "#ab47bc", // Plum RGBA
'#B2DFEE', // Sky Blue "#805451", // Mulberry
'#FFFFC2', // Parchment "#ff8c69", // Salmon
'#FDF5E6', // Old Lace "#ba55d3", // Magenta
"#b3ffba", // Mint RGBA
"#bae7b3", // Sea Green
"#b5d6fd", // Light Sky Blue
"#d9ecf1", // Mint Green RGBA
"#ffe6e6", // Light Pink
"#dcd0c8", // Linen
"#bed3f3", // Lavender Blush
"#ffe9f3", // Sand RGBA
"#333366", // Violet
"#993333", // Red Violet
"#3399ff", // Sky Blue
]; ];
let tags = [ const random_id = () => Math.round(Date.now() * Math.random()).toString();
{ name: "tag1", color: "blue" },
{ name: "tag2", color: "green" } const DEFAULT_TAGS = [
{ id: random_id(), name: "Plain Text", color: TAG_COLORS[0] },
{ id: random_id(), name: "OpenAI", color: TAG_COLORS[1] },
{ id: random_id(), name: "W++", color: TAG_COLORS[2] },
{ id: random_id(), name: "Boostyle", color: TAG_COLORS[3] },
{ id: random_id(), name: "PList", color: TAG_COLORS[4] },
{ id: random_id(), name: "AliChat", color: TAG_COLORS[5] },
]; ];
let tags = [];
let tag_map = {};
function loadTagsSettings(settings) {
tags = settings.tags !== undefined ? settings.tags : DEFAULT_TAGS;
tag_map = settings.tag_map !== undefined ? settings.tag_map : Object.create(null);
}
function getTagsList(key) {
if (!Array.isArray(tag_map[key])) {
tag_map[key] = [];
return [];
}
return tag_map[key].map(x => tags.find(y => y.id === x)).filter(x => x);
}
function getTagKey() {
if (selected_group) {
return selected_group;
}
if (this_chid) {
return characters[this_chid].avatar;
}
return null;
}
function addTagToMap(tagId) {
const key = getTagKey();
if (!key) {
return;
}
if (!Array.isArray(tag_map[key])) {
tag_map[key] = [tagId];
}
else {
tag_map[key].push(tagId);
}
}
function removeTagFromMap(tagId) {
const key = getTagKey();
if (!key) {
return;
}
if (!Array.isArray(tag_map[key])) {
tag_map[key] = [];
}
else {
const indexOf = tag_map[key].indexOf(tagId);
tag_map[key].splice(indexOf, 1);
}
}
function findTag(request, resolve) {
const skipIds = [...($("#tagList").find(".tag").map((_, el) => $(el).attr("id")))];
const haystack = tags.filter(t => !skipIds.includes(t.id)).map(t => t.name).sort();
const needle = request.term.toLowerCase();
const result = haystack.filter(x => x.toLowerCase().includes(needle));
resolve(result.length !== 0 ? result : [request.term]);
}
function selectTag(event, ui) {
let tagName = ui.item.value;
let tag = tags.find(t => t.name === tagName);
// create new tag if it doesn't exist
if (!tag) {
tag = createNewTag(tagName);
}
// unfocus and clear the input
$(this).val("").blur();
// add tag to the UI and internal map
appendTagToList("#tagList", tag, { removable: true });
addTagToMap(tag.id);
printTags();
saveSettingsDebounced();
// need to return false to keep the input clear
return false;
}
function createNewTag(tagName) {
const tag = {
id: random_id(),
name: tagName,
color: TAG_COLORS[Math.floor(Math.random() * TAG_COLORS.length)]
};
tags.push(tag);
return tag;
}
function appendTagToList(listSelector, tag, { removable, editable, selectable }) {
let tagElement = $('#tag_template .tag').clone();
tagElement.attr('id', tag.id);
tagElement.find('.tag_name').text(tag.name);
tagElement.css({ 'background-color': tag.color });
const removeButton = tagElement.find(".tag_remove");
removable ? removeButton.show() : removeButton.hide();
if (selectable) {
tagElement.on('click', onTagFilterClick);
}
// TODO: handle color change
$(listSelector).append(tagElement);
}
function onTagFilterClick() {
const wasSelected = $(this).hasClass('selected');
clearTagsFilter();
if (wasSelected) {
return;
}
const tagId = $(this).attr('id');
$(this).addClass('selected');
$('#rm_print_characters_block > div').each((_, element) => applyFilterToElement(tagId, element));
}
function applyFilterToElement(tagId, element) {
const isTagged = isElementTagged(element, tagId);
$(element).css('display', !isTagged ? 'none' : '');
}
function isElementTagged(element, tagId) {
const isGroup = $(element).hasClass('group_select');
const isCharacter = $(element).hasClass('character_select');
const idAttr = isGroup ? 'grid' : 'chid';
const elementId = $(element).attr(idAttr);
const lookupValue = isCharacter ? characters[elementId].avatar : elementId;
const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
return isTagged;
}
function clearTagsFilter() {
$('#rm_tag_filter .tag').removeClass('selected');
$('#rm_print_characters_block > div').css('display', '');
}
function printTags() {
$('#rm_tag_filter').empty();
const characterTagIds = Object.values(tag_map).flat();
const tagsToDisplay = tags.filter(x => characterTagIds.includes(x.id));
for (const tag of tagsToDisplay) {
appendTagToList('#rm_tag_filter', tag, { removable: false, editable: false, selectable: true, });
}
}
function onTagRemoveClick(event) {
event.stopPropagation();
const tag = $(this).closest(".tag");
const tagId = tag.attr("id");
tag.remove();
removeTagFromMap(tagId);
printTags();
saveSettingsDebounced();
}
function onTagInput(event) {
let val = $(this).val();
if (tags.find(t => t.name === val)) return;
$(this).autocomplete("search", val);
}
function onTagInputFocus() {
$(this).autocomplete('search', $(this).val());
}
$(document).on("click", ".character_select", function () {
clearTagsFilter();
const chid = Number($(this).attr('chid'));
const key = characters[chid].avatar;
const tags = getTagsList(key);
$("#tagList").empty();
for (const tag of tags) {
appendTagToList("#tagList", tag, { removable: true });
}
});
$(document).ready(() => { $(document).ready(() => {
$("#tagInput").autocomplete({ $("#tagInput")
source: tags.map(t => t.name), .autocomplete({ source: findTag, select: selectTag, minLength: 0, })
select: function(event, ui) { .focus(onTagInputFocus); // <== show tag list on click
let tagName = ui.item.value;
let tag = tags.find(t => t.name === tagName);
if (!tag) {
tag = {
name: tagName,
color: TAG_COLORS[Math.floor(Math.random() * colors.length)]
};
tags.push(tag);
}
let tagElement = $(`<span class="tag" style="background-color: ${tag.color}">${tag.name}</span>`);
$("#tagList").append(tagElement);
$(this).val("");
}
});
$("#tagInput").on("input", function(event) { $(document).on("click", ".tag_remove", onTagRemoveClick);
let val = $(this).val();
if (tags.find(t => t.name === val)) return; $("#tagInput").on("input", onTagInput);
$(this).autocomplete("search", val);
});
}); });

View File

@ -439,11 +439,6 @@ code {
background-color: var(--SmartThemeEmColor); background-color: var(--SmartThemeEmColor);
} }
#message_template {
display: none !important;
}
.auto_hide { .auto_hide {
content-visibility: auto; content-visibility: auto;
} }
@ -2323,11 +2318,39 @@ h5 {
box-sizing: border-box; box-sizing: border-box;
border-color: var(--black30a); border-color: var(--black30a);
padding: 0.2rem 0.3rem; padding: 0.2rem 0.3rem;
font-size: calc(var(--mainFontSize) * 1.1); font-size: calc(var(--mainFontSize) + 5%);
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
width: fit-content;
} }
#tagList { .tag.selected {
margin-top: 10px; font-weight: 500;
filter: brightness(125%);
}
.tag_remove {
cursor: pointer;
}
.tags {
margin: 10px 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
column-gap: 1rem;
row-gap: 0.5rem;
}
#rm_tag_filter {
justify-content: space-evenly;
}
#rm_tag_filter .tag {
cursor: pointer;
} }
body .ui-front { body .ui-front {
@ -2519,11 +2542,6 @@ body .ui-widget-content {
padding: 0px; padding: 0px;
} }
#group_member_template,
#group_list_template {
display: none !important;
}
.group_select { .group_select {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -2553,10 +2571,6 @@ body .ui-widget-content {
margin-left: 5px; margin-left: 5px;
} }
#typing_indicator_template {
display: none !important;
}
.typing_indicator { .typing_indicator {
position: sticky; position: sticky;
bottom: 10px; bottom: 10px;
@ -2600,10 +2614,6 @@ body .ui-widget-content {
} }
} }
#group_avatars_template {
display: none;
}
.avatar_collage { .avatar_collage {
border-radius: 50%; border-radius: 50%;
position: relative; position: relative;
@ -2950,6 +2960,7 @@ label[for="extensions_autoconnect"] {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 10px;
} }
.extensions_url_block h4 { .extensions_url_block h4 {
@ -3271,8 +3282,8 @@ toolcool-color-picker {
width: 100%; width: 100%;
} }
#openai_logit_bias_template { .template_element {
display: none; display: none !important;
} }
.openai_logit_bias_text, .openai_logit_bias_text,