diff --git a/public/script.js b/public/script.js
index 8421d264c..8ecfdbc17 100644
--- a/public/script.js
+++ b/public/script.js
@@ -93,6 +93,7 @@ import {
import { debounce, delay } from "./scripts/utils.js";
import { extension_settings, loadExtensionSettings } from "./scripts/extensions.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
export {
@@ -277,6 +278,8 @@ const system_messages = {
'',
'Type
/? in any chat to get help on message formatting commands.',
'
Still have questions or suggestions left?
',
+ '
SillyTavern Community Discord',
+ '
',
'
Post a GitHub issue.',
'
',
'
Contact the developers.'
@@ -667,9 +670,8 @@ function printCharacters() {
this_avatar = getThumbnailUrl('avatar', item.avatar);
} //RossAscends: changed 'prepend' to 'append' to make alphabetical sorting display correctly.
$("#rm_print_characters_block").append(
-
`
-
+
${item.name} ${item.fav == "true" ? '' : ''}
`
@@ -677,6 +679,7 @@ function printCharacters() {
//console.log('printcharacters() -- printing -- ChID '+i+' ('+item.name+')');
});
$("#rm_print_characters_block").prepend(`
`);
+ printTags();
printGroups();
sortCharactersList();
}
@@ -2825,6 +2828,8 @@ async function getSettings(type) {
// Load power user settings
loadPowerUserSettings(settings, data);
+ // Load- character tags
+ loadTagsSettings(settings);
//Enable GUI deference settings if GUI is selected for Kobold
if (main_api === "kobold") {
@@ -2918,10 +2923,12 @@ async function saveSettings(type) {
power_user: power_user,
poe_settings: poe_settings,
extension_settings: extension_settings,
+ tags: tags,
+ tag_map: tag_map,
...nai_settings,
...kai_settings,
...oai_settings,
- }),
+ }, null, 4),
beforeSend: function () {
//console.log('saveSettings() -- active_character -- '+active_character);
if (type == "change_name") {
@@ -3811,16 +3818,38 @@ $(document).ready(function () {
$("#character_search_bar").on("input", function () {
const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(',');
const searchValue = $(this).val().trim().toLowerCase();
+ const selectedTagId = $('#rm_tag_filter .tag.selected').attr('id');
if (!searchValue) {
- $(selector).show();
+ $(selector).each(function () {
+ if (selectedTagId && !isElementTagged(this, selectedTagId)) {
+ $(this).hide();
+ }
+ else {
+ $(this).show();
+ }
+ })
} else {
$(selector).each(function () {
- $(this).children(".ch_name").text().toLowerCase().includes(searchValue)
- ? $(this).show()
- : $(this).hide();
+ const isValidSearch = $(this).children(".ch_name").text().toLowerCase().includes(searchValue);
+
+ if (isValidSearch) {
+ if (selectedTagId && !isElementTagged(this, selectedTagId)) {
+ $(this).hide();
+ }
+ else {
+ $(this).show();
+ }
+ }
+ else {
+ $(this).hide();
+ }
});
}
+
+ if (selectedTagId) {
+ applyFilterToList(selectedTagId)
+ }
});
$("#filter_by_fav").click(function () {
diff --git a/public/scripts/tags.js b/public/scripts/tags.js
index dcdfe031e..74812441f 100644
--- a/public/scripts/tags.js
+++ b/public/scripts/tags.js
@@ -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 = [
- '#FFB6C1', // Light Pink
- '#FFECB3', // Light Yellow
- '#FFE4B5', // Cream
- '#B2EBF2', // Powder Blue
- '#E0FFFF', // Light Sky Blue
- '#FBB4B9', // Lavender
- '#FFF9C4', // Floral White
- '#DDA0DD', // Plum
- '#DA70D6', // Orchid
- '#D2B48C', // Tan
- '#FAEBD7', // Antique White
- '#FFEFD5', // Papaya Whip
- '#CD853F', // Peru
- '#B2DFEE', // Sky Blue
- '#FFFFC2', // Parchment
- '#FDF5E6', // Old Lace
+ "#dd0a36", // Red
+ "#ff6633", // Orange
+ "#5f9ea0", // Teal Green
+ "#1e90ff", // Light Blue
+ "#990066", // Plum
+ "#8c00ff", // Fuchsia
+ "#00ffff", // Aqua
+ "#0f4ecc", // Teal
+ "#2f4b1c", // Green
+ "#3366e5", // Dodger Blue
+ "#36c3a1", // Mint Green
+ "#995511", // Terracotta
+ "#ab47bc", // Plum RGBA
+ "#805451", // Mulberry
+ "#ff8c69", // Salmon
+ "#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 = [
- { name: "tag1", color: "blue" },
- { name: "tag2", color: "green" }
+const random_id = () => Math.round(Date.now() * Math.random()).toString();
+
+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(() => {
- $("#tagInput").autocomplete({
- source: tags.map(t => t.name),
- select: function(event, ui) {
- 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 = $(`
${tag.name}`);
- $("#tagList").append(tagElement);
- $(this).val("");
- }
- });
-
- $("#tagInput").on("input", function(event) {
- let val = $(this).val();
- if (tags.find(t => t.name === val)) return;
- $(this).autocomplete("search", val);
- });
+ $("#tagInput")
+ .autocomplete({ source: findTag, select: selectTag, minLength: 0, })
+ .focus(onTagInputFocus); // <== show tag list on click
+
+ $(document).on("click", ".tag_remove", onTagRemoveClick);
+
+ $("#tagInput").on("input", onTagInput);
});
\ No newline at end of file
diff --git a/public/style.css b/public/style.css
index 808168003..f2d7487d8 100644
--- a/public/style.css
+++ b/public/style.css
@@ -439,11 +439,6 @@ code {
background-color: var(--SmartThemeEmColor);
}
-
-#message_template {
- display: none !important;
-}
-
.auto_hide {
content-visibility: auto;
}
@@ -2323,11 +2318,39 @@ h5 {
box-sizing: border-box;
border-color: var(--black30a);
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 {
- margin-top: 10px;
+.tag.selected {
+ 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 {
@@ -2519,11 +2542,6 @@ body .ui-widget-content {
padding: 0px;
}
-#group_member_template,
-#group_list_template {
- display: none !important;
-}
-
.group_select {
display: flex;
flex-direction: row;
@@ -2553,10 +2571,6 @@ body .ui-widget-content {
margin-left: 5px;
}
-#typing_indicator_template {
- display: none !important;
-}
-
.typing_indicator {
position: sticky;
bottom: 10px;
@@ -2600,10 +2614,6 @@ body .ui-widget-content {
}
}
-#group_avatars_template {
- display: none;
-}
-
.avatar_collage {
border-radius: 50%;
position: relative;
@@ -2950,6 +2960,7 @@ label[for="extensions_autoconnect"] {
display: flex;
align-items: center;
justify-content: space-between;
+ gap: 10px;
}
.extensions_url_block h4 {
@@ -3271,8 +3282,8 @@ toolcool-color-picker {
width: 100%;
}
-#openai_logit_bias_template {
- display: none;
+.template_element {
+ display: none !important;
}
.openai_logit_bias_text,